Wrapping up boilerplate code — part 1: Interface builder
Working as full time iOS app developer, I would like to share few tips on ‘How to wrap boilerplate code for interface builder in swift’.
Most of us have time constraints and have to deliver projects ASAP. This led us to copy-paste code and develop redundant codebase. Copy-pasting seems easy solution first, but as project grows, it becomes less readable and hard to maintain. So let’s take a quick look here.
Using Storyboards
We’re familiar with instantiating view controllers from storyboard, i.e
let storyboard = UIStoryboard(name: “Main”, bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: “ProfileViewController”) as! ProfileViewController
Now let’s simplify the above code:-
- Our view controller will tell which
storyboard
they are defined, - and also its
storyboardId
.
So goal is to write ProfileViewController.instantiate()
.
We need extension: UIViewControlle
for this. And we’ll use class func
, similar to static func
. But class func
comes with override
facility, and that is going to help us to achieve our goal. @objc
is required for overriding the variable.
extension UIViewController {
@objc class var fromStoryboard: UIStoryboard? {
return nil
}
@objc class var storyboardId: String {
return String(describing: self)
}
}
Now we just need to provide the fromStoryboard
to find it where. storyboardId
is optional, it’s default implementation assumes that we have class name as storyboardId
. String(describing: self)
by default gives the exact class name in String. self
in class function is class type itself.
Finally let’s implement instantiate()
.
Self
is Generic class type and it denotes from which class is used to call the method. Here comes the time to utilise instantiate()
method. We should create UIStoryboard
objects somewhere else to easily access them.
class MyStorybaords {
static var main = UIStoryboard(name: “Main”, bundle: nil)
static var profile = UIStoryboard(name: “Profile”, bundle: nil)
....
}class ProfileViewController: UIViewController {
override class var fromStoryboard: UIStoryboard? {
return MyStorybaords.profile
}
}
Voila, Thats all! Now we can instantiate view controllers by writing simply
let viewController = ProfileViewController.instantiate()
Using xib to load UIViews
With a little modification, the same can be used for instantiating UIView
from xib
files. Again method initViewFromNib()
assumes same filename is same to class name. Leaving you to add flexibility.
Another example of UITableView
reusable cell dequeue and register
Here we don’t need static
or class
function, since we would have UITableView
object. Parameter type
takes the cell type and we can get the cell by its class name. Same can be applied to UICollectionView
.
let cell = tableView.dequeueReusableCell(type: MyCustomCell.self)
Using IBDesignable UIView from Xib
Let’s see another instance when we use @IBDesignable
to instantiate views from xib
files, we can use inheritance to wrap those codes.
Now we can inherit from UIViewXib and design view in xib
file with no extra code for loading views from xib
.
class DayView: UIViewXib {
// other things...
}
Use this DayView
to another view in interface builder. UIStackView
is used to add 7 DayView
objects.
More optimised version of UIViewXib is here.(Assuming Autolayout is used to create xib files.)
Thats all for today flocks.
Keep improving yourself until you reach your goal. Even a small change can make huge difference.
Thanks for reading, feedback are always welcome.