Komponents is a Swift framework for building component-oriented interfaces.
Because it's unfair to need javascript to enjoy Components ! 😎
Building a Loading screen with Hot reload 🎩
func render() -> Node {
return Label("Hello, Component !")
}
New to components? Fear not! Facebook's React guide is a gold mine of information to get you started :)
Komponents | |
---|---|
🔶 | Pure Swift (no JS, no XML) |
🏗 | Can be used Incrementally in your classic UIKit App |
📐 | Can use Autolayout or any autolayout lib for the layout (we like Stevia) |
💉 | Supports Hot Reload with 💉 injectionForXcode |
A component is pretty simple :
- It has a
render
function that returns aNode
. - It has a
state
property.
That's All!
import Komponents
class MyFirstComponent: Component {
var state = MyState()
func render() -> Node {
return Label("Hello!")
}
}
To use a component as a UIViewController
and play nicely with UIKit apis, just subclass
UIViewController
and call loadComponent
in loadView
:)
class LoadingScreen: UIViewController, Component {
// Just call `loadComponent` in loadView :)
override func loadView() { loadComponent() }
func render() -> Node {
return ...
}
}
You can now push
and present
your view controller like you used to, except this is now a powerful component! 😎
This is particularly handy to start migrating parts of the App to using components without breaking everything!
To use a component as a UIView
and play nicely with UIKit apis, just subclass
UIView
and call loadComponent
in an init
function :)
class MyCoolButton: UIView, Component {
// Here we load the component
convenience init() {
self.init(frame:CGRect.zero)
loadComponent()
}
func render() -> Node {
return ...
}
}
This way you have classic UIView
that behaves like a component! 💪
The idea is that you will compose your own components out of the default nodes provided.
The naming is pretty simple, take the UIKit name and remove UI
prefix.
UIView
becomes View
, UIButton
Button
, etc... you get it :)
Here is the complete list of provided nodes :
View
Label
Field
(TextField)
TextView
Button
Image
ScrollView
PageControl
ActivityIndicatorView
VerticalStack
HorizontalStack
Slider
Switch
ProgressView
SegmentedControl Stepper TableView CollectionView TableViewCell CollectionViewCell DatePicker PickerView VisualEffectView MapKitView Webview TapGestureRecognizer PinchGestureRecognizers RotationGestureRecognizers SwipeGestureRecognizers Toolbar SearchBar
Every node follows the same convention :
Node(ContentType,
style: ((NodeType) -> Void),
layout: ((NodeType) -> Void),
ref: UnsafeMutablePointer<NodeType>,
children: [Renderable])
For example a Label
:
Label("My first Label",
style: { $0.textColor = .red },
layout: { $0.centerInContainer() },
ref: &lablel,
children: [
// Add Child nodes here
])
What's cool is that everything is optional so that you can just write :
Label("My first Label")
The first parameter is the content, for instance an image for an Image
node, some text for a Label
.
The style parameter is where you customize the UIKit
object.
Just style your object however you want, say modify it's backgroundColor
!
Notice we strongly separated the layout
and the style
parameters.
Though they are both called with the UIKit object, we believe it's clearer to seperate what is styling from what is in the layout domain.
Once again we want to play nice with UIKit and in this block you can use pure Autolayout. We want to stress this point because it's a major advantage of this tool. We like to use Stevia, but be free to use whatever layout library that suits your needs !
This will come very useful for falling back to the classic way of doing things.
class MyComponent: StatelessComponent {
var labelReference = UILabel()
func render() -> Node {
return Label("My text", ref: &labelReference)
}
func foo() {
// Later you can access the label and fall back to the old way of doing thing!
labelReference.text = "Yay I'm not stuck in this !"
}
}
This is just an array of the nested nodes.
Display your component in a UIView and use it wherever You want!
let view = ComponentView(component: MyComponent())
Embbed your component in view Controller and present it anyway you want :)
let vc = ComponentVC(component: MyComponent())
func render() -> Node {
let items = ["Hello", "How", "Are", "You?"]
return
View(style: { $0.backgroundColor = .white }, [
VerticalStack(style: { $0.spacing = 40 }, layout: { $0.centerInContainer() },
items.map { Label($0) }
)
])
}
import UIKit
import Stevia
import Komponents
class LoadingScreen: UIViewController, StatelessComponent {
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
override func loadView() { loadComponent() }
func render() -> Node {
return
View(style: { $0.backgroundColor = .darkGray }, [
HorizontalStack(style: { $0.spacing = 8 }, layout: {$0.centerInContainer() }, [
Label("Loading...", style: { $0.textColor = .white }),
ActivityIndicatorView(.white, style: { $0.startAnimating() })
])
])
}
}
Take a look at the example Project KomponentsExample.xcodeproj
and play around !
Like React, Komponents tries to be smart about what it rerenders when the state changes.
By default only the component that changed is re-rendered.
Say you have the nested components A
>B
>C
and C
's state changes, only C
is going to rerender.
But the whole C component is going to be re-rendered, and maybe only some text, or some color changed, and the structure of it is pretty much the same. That's where patching comes in!
Patching is going to rerender the component in a background thread, compare the new view hierarchy with the old one, and batch the updates on the UIThread.
Patching is still experimental but can only get better with time!
You can opt-in by implementing enablePatching()
in your component :
func enablePatching() -> Bool {
return true
}
View: backgroundColor
Label: text
, textColor
Image: image
Button: title
(state normal), backgroundImage
(state normal), isEnabled
github "s4cha/Komponents"
Simply Copy and Paste all the .swift
files from the Source
folder in your Xcode Project :)
Coming soon
YannickDot,
S4cha, YOU ?!
We'd love to hear what you think so don't hesitate to reach out through an issue or via twitter
@sachadso
Facebook's React, ComponentKit, Preact, Vue.js AlexDrone's render, Angular... Pure Swift React/ ComponentKit implementation
We're not the first to tackle the great endeavor of swift components and here are some other very cool projects :