Swift Protocols are awesome. Understanding how they can (or should) fit into our code can be tricky. Today we'll take a baby step into the world of protocols with a simple, but "real" example and try to illustrate the upsides. Let's get started.
We're going to be fancy and abstract away some of our layout code. So we'll create a little struct to hold some layout settings like this:
struct LayoutSettings {
let direction: FlexDirection
let justification: Justification
let alignmentSelf: Alignment
let alignmentChildren: Alignment
/// ...etc
}
See? Fancy. This is great if we want to specify each individual combination each time, but it'd be nice if we could define some sort of "pre-canned" layouts that we could use by name. Sounds like a great job for a Swift Enum.
enum CannedLayout {
case FillParent
case SizeToFit
case Absolute(point: CGPoint)
case Relative(closure: (parentFrame: CGRect) -> CGSize)
}
Lovely, this will be handy. How are we going to wire all this together though? Simple, we'll make a Protocol that's only responsibility is to convert itself to a LayoutSettings.
protocol LayoutSettingsConvertible {
func layoutSettings() -> LayoutSettings
}
LayoutSettings can adopt this extremely simply:
extension LayoutSettings : LayoutSettingsConvertible {
func layoutSettings() -> LayoutSettings { return self }
}
Whew! That was tough.
Making our CannedLayout Enum adopt our new Protocol is a bit more involved, but really just means switch
-ing over self
and return the proper combination of settings for each case.
extension CannedLayout : LayoutSettingsConvertible {
func layoutSettings() -> LayoutSettings {
switch self {
case .FillParent: return LayoutSettings(direction: .Vertical, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start)
/// ...etc
}
}
}
All that's left is to use this new protocol somewhere. Let's wire this up to UIView to make it useful:
extension UIView {
func layout(settings: LayoutSettingsConvertible) {
/// configure the view for the new settings here
}
}
Neat! Now, we can use configure views with one of our canned layouts:
let v = UIView(frame: .zero)
v.layout(CannedLayout.FillParent)
But we can also easily configure them the "long way" using a full LayoutSettings object directly:
let v = UIView(frame: .zero)
v.layout(LayoutSettings(direction: .Vertical, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start))
Now that we have this simple protocol, we can make other helper types like this:
struct Row : LayoutSettingsConvertible {
func layoutSettings() -> LayoutSettings {
return LayoutSettings(direction: .Horizontal, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start)
}
}
That's just the basics when it comes to Protocols. They have much more to offer. More on this topic soon!