Skip to content

andris-zalitis/swift-elm-architecture

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

The Elm Architecture for Swift

Elm Logo Swift Logo Continuous Integration

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.

Example

Let's build a counter:

Screenshot

Functional core

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)
    }
    
}

Imperative shell

Storyboard

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
    }
    
}

Unit tests

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))
    }
    
}

Installation

  • Add github "salutis/swift-elm-architecture" to Cartfile
  • Run carthage update swift-elm-architecture
  • Drag Carthage/Build/iOS/Elm.framework to Xcode project
    • Targets:
      • App: Yes
      • AppTests: Yes
  • Add Run Script build phase to both App and AppTests targets
    • Script: carthage copy-frameworks
    • Input files:$(SRCROOT)/Carthage/Build/iOS/Elm.framework

About

The Elm Architecture for Swift

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Swift 99.8%
  • Shell 0.2%