Search code examples
swiftgenericsselfinitializer

How to write a generic Swift class function to initialize classes using a closure?


I'm trying to write a generic class function for Swift classes that would allow me to initialize classes using trailing closure syntax.

I have already got it working for specific classes, like for example UILabel.

// Extension for UILabel
extension UILabel {
    class func new(_ initialization: (inout UILabel) -> Void) -> UILabel {
        var label = UILabel()
        initialization(&label)
        return label
    }
}

// Initialize new UILabel using trailing closure syntax and "new" function
let label = UILabel.new {
    $0.textColor = .red
}

However, I want to have this functionality for all subclasses of NSObject, so I'm trying to implement a generic version of the "new" function above. So far I have come up with this:

extension NSObject {
    class func new(_ initialization: (inout Self) -> Void) -> Self {
        var newSelf = Self()
        initialization(&newSelf)
        return newSelf
    }
}

But this produces the following error: 'Self' is only available in a protocol or as the result of a method in a class; did you mean 'NSObject'?

I am trying this in a playground with Swift 5.1 (Xcode 11 beta).


Solution

  • As Hamish said, maybe it's better to leave the init outside so it is more flexible. However there could be a kind of workaround for this using a protocol.

    protocol Initializable {
        init()
    }
    
    extension Initializable {
        static func new(_ initialization: (inout Self) -> Void) -> Self {
            var newSelf = Self()
            initialization(&newSelf)
            return newSelf
        }
    }
    
    extension NSObject: Initializable {}
    

    NSObject already have an init so it automatically conforms to Initializable. Then write your extension on the protocol.

    The only thing to be aware is that you cannot use class modifier now, as you're in a protocol.

    Not sure if this can lead to problem for the NS_UNAVAILABLE modifier, I think it could crash at runtime when you use this on a class that is hiding the init.