The Elm Architecture is a simple pattern for architecting apps. It is great for modularity, code reuse, and testing. Ultimately, it makes it easy to create complex apps that stay healthy as you refactor and add features.
Let's build a counter:
import Elm
struct Counter: Program {
enum Event {
case userDidTapIncrementButton
case userDidTapDecrementButton
}
typealias State = Int
typealias View = String
static func start(with seed: Void) -> Start<Counter> {
return .next(state: 0)
}
static func update(for event: Event, state: State) -> Update<Counter> {
switch event {
case .increment: return .next(state: state + 1)
case .decrement: return .next(state: state - 1)
}
}
static func render(with state: State) -> Render<Counter> {
let view = String(state)
return .view(view)
}
}
import UIKit
import Elm
class CounterViewController: UIViewController, StoreDelegate {
typealias Program = Counter
var store: Store<Program>!
@IBOutlet var countLabel: UILabel!
@IBOutlet var incrementButton: UIBarButtonItem!
@IBOutlet var decrementButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
store = Counter.makeStore(delegate: self)
}
@IBAction func userDidTapIncrementButton() {
store.dispatch(.userDidTapIncrementButton)
}
@IBAction func userDidTapDecrementButton() {
store.dispatch(.userDidTapDecrementButton)
}
func store(_ store: Store<Program>, didUpdate view: Program.View) {
countLabel.text = view
}
}
import XCTest
import Elm
@testable import Counter
class CounterTests: XCTestCase, Tests {
typealias Program = Counter
func testStart() {
let start = program.start()
let state = start.expectState()
expect(state, equals: 0)
}
func testIncrement1() {
let update = program.update(for: .userDidTapIncrementButton, state: 1)
let state = update.expectState()
expect(state, equals: 2)
}
func testIncrement2() {
let update = program.update(for: .userDidTapIncrementButton, state: 2)
let state = update.expectState()
expect(state, equals: 3)
}
func testDecrement1() {
let update = program.update(for: .userDidTapDecrementButton, state: -1)
let state = update.expectState()
expect(state, equals: -2)
}
func testDecrement2() {
let update = program.update(for: .userDidTapDecrementButton, state: -2)
let state = update.expectState()
expect(state, equals: -3)
}
func testView1() {
let render = program.render(with: 1)
let view = render.expectView()
expect(view, equals: "1")
}
func testView2() {
let render = program.render(with: 2)
let view = render.expectView()
expect(view, equals: "2")
}
func fail(_ message: String, file: StaticString, line: Int) {
XCTFail(message, file: file, line: UInt(line))
}
}
- Add
github "salutis/swift-elm-architecture"
toCartfile
- Run
carthage update swift-elm-architecture
- Drag
Carthage/Build/iOS/Elm.framework
to Xcode project- Targets:
App
: YesAppTests
: Yes
- Targets:
- Add Run Script build phase to both
App
andAppTests
targets- Script:
carthage copy-frameworks
- Input files:
$(SRCROOT)/Carthage/Build/iOS/Elm.framework
- Script: