Protocols
The following protocols are available globally.
-
A protocol that views should conform to.
Its main goal is to help structure the code in a more clean, organized way.
Example
/// An image based checkbox. open class SKCheckbox: UIControl, BMView { // MARK: - Properties /// The currently displayed checkbox Image. private var checkboxImage = UIImageView() /// Overriding the original method to update the icon based on state. open override var isEnabled: Bool { didSet { self.update() } } // MARK: - Interactions public var valueDidChange: ((_ checkbox: SKCheckbox, _ oldValue: Bool) -> Void)? // MARK: - init /// `init` via code. public override init(frame: CGRect) { super.init(frame: frame) self.configure() self.update() self.layout() } /// `init` via IB. public required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.configure() self.update() self.layout() } // MARK: - CSUL public func configure() { self.addSubview(self.checkboxImage) } public func style() { // style the default looks of the view and its subviews. } public func update() { // update UI when isEnabled changes status, among other things. } public func layout() { // layout the `checkboxImage` and other potential subviews. } }
The view exposes properties and interactions to the outside world. The properties deal with the state of the element, whereas the interactions are callbacks to interact with changes inside the element. The lifecycle of the view is composed of
configure
,style
,update and
layout`.Configure
Executed once when the view is instatiated. It deals with configuring the view. For instance, a typical usecase would be to add subviews, or setup interactions.
func configure() { self.addSubiew(sel.checkboxImage) }
Style
Executed once when the view is instatiated. It deals with applaying the basic static styling to the view. In other words element properties that are independent of the state of the element are applied here. If the background of the element always has the same color, the background color is set here.
func style() { self.backgroundColor = .white }
Update
Update is not directly called when the object is instantiated but rather when a state related value changes its state. Inside of the update, changes should be made to the UI to reflect the new state.
open override var isEnabled: Bool { didSet { self.update() } }
Layout
Excecuted once when the view is instatiated. It layouts the subviews as intended.
See morefunc layout() { self.checkboxImage.frame = self.bounds
Declaration
Swift
public protocol BMView : AnyObject
-
An object, preferably a struct, containing a set of properties to be passed to a
BMViewWithViewModel
. These properties should help the view render itself to reflect proper states.struct ProfileViewModel: BMViewModel { let name: String let age: Int = 27 }
Declaration
Swift
public protocol BMViewModel
-
A
See moreBMViewWithViewControllerAndViewModel
is aBMViewWithViewModel
view managed by aUIViewController
. This “managing” automatically calls theconfigure
,style
andlayout
phases. In addition, the controller set theviewModel
of the view, therefore invoking the update at least once before the view is actually displayed. This view is extended with helpers allowing to access the UINavigationBar, UINavigationItem, UIViewController from the view when present.Declaration
Swift
public protocol BMViewWithViewControllerAndViewModel : BMViewWithViewModel
-
A BMViewWithViewModel takes the BMView and UIView a step further by introducing a view model. The view model is an object containing a set of properties reflecting the state of the view. A view model makes it easier to mock a view by passing the state from the outside and therefore test it in any desired state.
Example
struct ProfileViewModel: BMViewModel { let name: String let age: Int var isAdult: Bool { age > 18 } var badgeColor: UIColor { isAdult ? .green : .red } }
class ProfileView: BMViewWithViewModel { let badge = UIView() let nameLabel = UILabel() func configure() { self.addSubview(self.badge) self.addSubview(self.nameLabel) } func style() { // style the elements } func update(oldViewModel: ProfileViewModel?) { self.badge.backgroundColor = self.viewModel?.badgeColor self.nameLabel.text = self.viewModel?.name } func layout() { // layout the subviews } }
By conformimng to
BMViewWithViewModel
in this previous example, we notice we do not explicetely need to specify the viewModel since it is infered by swift through theupdate(oldViewModel: VM?)
method.By altering the view model, different states of the view can be tested in a fast efficient way.
See moreDeclaration
Swift
public protocol BMViewWithViewModel : BMView
-
A protocol requiring its conformer to declare its
See moreUIViewController
Hierarchy. Any customUIViewController
other thanUINavigationController
,UITabBarController
containing a custom navigation should conform to this protocol.Declaration
Swift
public protocol InspectableHierarchy : AnyObject
-
A protocol that only
See moreUIViewController
should conform to to returnm thepresentedViewController
if present.Declaration
Swift
public protocol InspectablePresentedViewController : AnyObject
-
A
See moreprotocol
that all navigableViewControllers
should adhere to. TheUINavigationController
and theUITabBarController
should NOT adhere to this protocol.Declaration
Swift
public protocol Routable : AnyObject
-
See moreTypeErasure
forRoutableObject<VM: BMViewModel>
Declaration
Swift
public protocol AnyRoutableObject