Search code examples
swiftstructprotocolsdynamictypeassociated-types

Swift dynamictype initialisation with dynamic protocol type


I have a number of structs which implement a Resource protocol. This defines that they must have a variable extendedInfo which conforms to ExtendedInfo protocol to provide a way to initialise them with json via init(json: [String: AnyObject]. I'm trying to provide a way to dynamically instantiate these, with JSON, providing the right type of ExtendedInfo and assign it to the struct's extendedInfo variable. However, I'm getting a Argument labels '(json:)' do not match any available overloads error when trying to instantiate them via their dynamicType

protocol Resource {

    associatedtype ExtendedInfoTypeAlias: ExtendedInfo

    var extendedInfo: ExtendedInfoTypeAlias? { get set }
}

protocol ExtendedInfo {
    init(json: [String: AnyObject])
}

struct User: Resource {

    typealias ExtendedInfoTypeAlias = UserExtendedInfo

    let name: String = "Name"
    var extendedInfo: UserExtendedInfo?
}

struct UserExtendedInfo: ExtendedInfo {

    let age: Int?

    init(json: [String: AnyObject]) {
        age = json["age"] as? Int
    }
}


let user = User()
let sampleJSON = ["age": 50]

let userExtendedInfo = user.extendedInfo.dynamicType.init(json: sampleJSON) // Argument labels '(json:)' do not match any available overloads
user.extendedInfo = userExtendedInfo

Any ideas guys? Thanks


Solution

  • First of all, you don't need to explicitly define the type of ExtendedInfoTypeAlias in your struct implementation – you can just let it be inferred by the type you provide for extendedInfo.

    struct User: Resource {
        let name: String = "Name"
        var extendedInfo: UserExtendedInfo?
    }
    

    Second of all, you can just use the protocol's associated type of your given struct's dynamicType in order to use your given initialiser. For example:

    user.extendedInfo = user.dynamicType.ExtendedInfoTypeAlias.init(json: sampleJSON)
    print(user.extendedInfo) // Optional(Dynamic_Protocols.UserExtendedInfo(age: Optional(50)))
    

    As for why your current code doesn't work, I suspect it's due to the fact that you're getting the dynamicType from an optional – which is preventing you from calling your initialiser on it.


    I did find that the following works, even when extendedInfo is nil. (This is a bug).

    user.extendedInfo = user.extendedInfo!.dynamicType.init(json: sampleJSON)