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
}
}