Skip to content

Instantly share code, notes, and snippets.

@xtravar
Last active February 29, 2016 16:33
Show Gist options
  • Save xtravar/8f28daf8286412af9758 to your computer and use it in GitHub Desktop.
Save xtravar/8f28daf8286412af9758 to your computer and use it in GitHub Desktop.
Creatable Swift extension

The Problem

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.

Clever Workaround

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.

The Create Extension

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
    }
    

Extension Code

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
        }
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment