Search code examples
swiftinitializationcreation

Instantiate Self outside of init in Swift


I want to create a class, so that the it's subclasses after calling a function a, will receive a new object of the type Self. I'm insuring, that the subclasses will have the init() method.

In a way I want to clone the object, but actually it's more than that, since I want to create a clone with modified values of the original, so I don't really want to use swifty copy constructor syntax

Why it doesn't work? Definitely this:

func myCustomCopy(modificationCommand: Command) -> Test {
    let newInt = modificationCommand.execute(self.myInt)
    return Test(newInt: newInt)
}

is not what I want.

Example:

protocol Testable {
    var myInt: Int { get set }
    init(newInt: Int)
}

class Test: Testable {
    var myInt = 10
    required init(newInt: Int) { myInt = newInt }

    func myCustomCopy(modificationCommand: Command) -> Self {
        let newInt = modificationCommand.execute(self.myInt)
        return self.init(newInt: newInt)
    }
}

Solution

  • You may use the (dynamically typed) metatype returned by type(of:) to access an initializer of the concrete type of the metatype. Quoting the Language Reference - Metatypes

    Use an initializer expression to construct an instance of a type from that type’s metatype value. For class instances, the initializer that’s called must be marked with the required keyword or the entire class marked with the final keyword.

    So in your case, you could use the metatype of self to call a required initializer of the concrete type of self, e.g.

    func myCustomCopy() -> Self {
        return type(of: self).init()
    }
    

    Note that, as specified in the quote above, since you are working with a non-final class, the initializer must be a required one.