Skip to content

Instantly share code, notes, and snippets.

@yoching
Last active April 9, 2023 19:54
Show Gist options
  • Save yoching/8d44eb0016df020dbd8703e8b196c943 to your computer and use it in GitHub Desktop.
Save yoching/8d44eb0016df020dbd8703e8b196c943 to your computer and use it in GitHub Desktop.
Functional view style sample
import UIKit
import PlaygroundSupport
// 1. define style as function
typealias ViewStyle = (UIView) -> UIView
let cornerRadius10: ViewStyle = { view -> UIView in
view.layer.cornerRadius = 10.0
return view
}
let backgroundColorOrange: ViewStyle = { view -> UIView in
view.backgroundColor = .orange
return view
}
// cornerRadius10(backgroundColorOrange(someView))
// 2. make syntax readable (pipe operator)
precedencegroup ForwardApplication {
associativity: left
}
infix operator |>: ForwardApplication
func |> (view: UIView, style: ViewStyle) -> UIView {
return style(view)
}
/*
someView
|> backgroundColorOrange
|> cornerRadius10
*/
// 3. make parameter configurable (currying)
let cornerRadiusNotCurried: (CGFloat, UIView) -> UIView = { radius, view -> UIView in
view.layer.cornerRadius = radius
return view
}
// view |> cornerRadius doesn't work
// currying
let cornerRadius: (CGFloat) -> ViewStyle = { radius -> ViewStyle in
return { view -> UIView in
view.layer.cornerRadius = radius
return view
}
}
cornerRadius(5.0) // (UIView) -> UIView, ViewStyle
// view |> cornerRadius(5.0) works
let backgroundColor: (UIColor) -> ViewStyle = { color -> ViewStyle in
return { view -> UIView in
view.backgroundColor = color
return view
}
}
// 4. make same style reusable (function composition)
func compose(style1: @escaping ViewStyle, style2: @escaping ViewStyle) -> ViewStyle {
return { view -> UIView in
return style2(style1(view)) // view |> style1 |> style2
}
}
// let cardStyle = compose(cornerRadius(5.0), backgroundColor(.orange))
// let someComplexStyle = compose(cornerRadius(5.0), compose(backgroundColor(.orange), ...))
infix operator >>>: ForwardApplication
func >>> (style1: @escaping ViewStyle, style2: @escaping ViewStyle) -> ViewStyle {
return { view -> UIView in
return view |> style1 |> style2
}
}
// 5. generalize custom operators
func |> <A, B> (lhs: A, f: (A) -> B) -> B {
return f(lhs)
}
func >>> <A, B, C> (f1: @escaping (A) -> B, f2: @escaping (B) -> C) -> (A) -> C {
return { a -> C in
return a |> f1 |> f2
}
}
class ViewController: UIViewController {
override func loadView() {
self.view = UIView()
self.view.backgroundColor = .darkGray
let someView = UIView()
someView.frame = CGRect(x: 37.5, y: 200, width: 300, height: 300)
// 1
cornerRadius10(backgroundColorOrange(someView))
// 2
let orangeView: UIView = someView |> backgroundColorOrange
orangeView |> cornerRadius10
someView
|> backgroundColorOrange
|> cornerRadius10
/*
someView
. backgroundColorOrange
. cornerRadius10
*/
// 3
someView
|> cornerRadius(5.0)
|> backgroundColor(.orange)
view.addSubview(someView)
// 4
let cardStyle = cornerRadius(5) >>> backgroundColor(.orange)
someView |> cardStyle
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = ViewController()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment