swiftstructmetatype

Pass Metatype as function argument


In Swift I can do the following:

struct Employee{
    var name:String
    var age:Int
}

// Metatype
let currentType = Employee.self
// concrete instance
let instanceFromType = currentType.init(name: "Jessie", age: 54)

print(instanceFromType) // prints Employee(name: "Jessie", age: 54)

currentType is a Metatype: this means I could pass another struct name (eg. Person, etc.) and instanceFromType would contain a struct of another type.

But, suppose I want to pass currentType as a function argument and then, inside the body of the function, create instanceFromType: how would I do?

I tried this one:

func f(m:Any.Type){

  let instanceFromType = m.init(name: "Jessie", age: 54)
  print(instanceFromType)
}

f(m:currentType)

but I get:

'init' is a member of the type; use 'type(of: ...)' to initialize a new object of the same dynamic type

What am I doing wrong? Any help is appreciated.

[UPDATE]

I forgot to mention that I found this one is working, but I really can't understand why:

protocol Proto {
    init(name:String,age:Int)
}

struct Employee:Proto{
    var name:String
    var age:Int
    init(name:String,age:Int){
        self.name = name
        self.age = age
    }
}

let currentType = Employee.self

func f(m:Proto.Type){

    let instanceFromType = m.init(name: "Jessie", age: 54)
    print(instanceFromType)

}

f(m:currentType)

Solution

  • You cannot call m.init(name: "Jessie", age: 54) for an arbitrary type m, because the type does not necessarily have such an initializer.

    What you can do is to define a protocol for a type which can be initialized from those arguments, and restrict the argument of f accordingly:

    protocol InitializableFromNameAndAge {
        init(name: String, age: Int)
    }
    
    func f(type: InitializableFromNameAndAge.Type) {
        let instance = type.init(name: "Jessie", age: 34)
        print(instance)
    }
    

    Then declare protocol conformance for your types

    struct Employee: InitializableFromNameAndAge {
        var name:String
        var age:Int
    }
    

    and then

    let currentType = Employee.self
    f(type: currentType)
    

    works as expected.