I really like setting properties in their declaration line. It just feels so good to leverage Swift's let.
The drawback is that you must then set properties' properties in init, somewhat splitting the "setup" into two stages and mixing it with the "usage".
public class MyView : UIView {
private let titleLabel = UILabel()
public init(frame: CGRect) {
super.init(frame: frame)
self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
self.titleLabel.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
// constraints, etc
}
// so on and so forth
}
I really wanted all of my property initialized at the same time so it would be ready by init. Some options include custom init extensions, custom factory methods, etc. I'm not a huge fan of going that route and generating a ton of code.
One neat-o way of solving this is to create a block inline and call it. {}()
public class MyView : UIView {
private let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
return label
}()
// so on and so forth, add view constraints in init
}
This helps, but it could be a lot nicer. I'm sure Swift might have something built-in someday, but we can leverage its powerful extension mechanism until then.
With the extension below, I've solved all of my property init woes.
- The type doesn't need specifying - it's inferred from the create method
- An extra variable is unnecessary - $0 will suffice
- Calling init() is done automatically and passed to create
- A trailing closure is prettier than a closer with call syntax
- The property is immediately ready to be used once my class's init is called
public class MyView : UIView {
private let titleLabel = UILabel.create {
$0.font = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
}
// so on and so forth, add view constraints in init
}
This can easily be improved or expanded to suite your needs, possibly regarding nillability and try/throw, if needed.
extension NSObject : Creatable {
}
// can't use 'Self' in extension of NSObject, so have to use a protocol
public protocol Creatable {
init()
}
public extension Creatable {
public static func create(creatorFunc: (Self)->Void) -> Self {
let retval = self.init()
// you may or may not want this - some APIs require it as true
if let view = retval as? UIView {
view.translatesAutoresizingMaskIntoConstraints = false
}
creatorFunc(retval)
return retval
}
}