Skip to content

Commit

Permalink
Add ConnectedView, StoreConnector and StoreProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
Dimillian committed Jul 23, 2019
1 parent 0fc5d59 commit ccbafdd
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 0 deletions.
28 changes: 28 additions & 0 deletions Sources/SwiftUIFlux/connector/ConnectedView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// File.swift
//
//
// Created by Thomas Ricouard on 23/07/2019.
//

import SwiftUI

public protocol ConnectedView: View {
associatedtype State: FluxState
associatedtype Props
associatedtype V: View

func map(state: State, dispatch: @escaping (Action) -> Void) -> Props
func body(props: Props) -> V
}

public extension ConnectedView {
func render(state: State, dispatch: @escaping (Action) -> Void) -> V {
let props = map(state: state, dispatch: dispatch)
return body(props: props)
}

var body: StoreConnector<State, V> {
return StoreConnector(content: render)
}
}
17 changes: 17 additions & 0 deletions Sources/SwiftUIFlux/connector/StoreConnector.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// File.swift
//
//
// Created by Thomas Ricouard on 23/07/2019.
//

import SwiftUI

public struct StoreConnector<State: FluxState, V: View>: View {
@EnvironmentObject var store: Store<State>
let content: (State, @escaping (Action) -> Void) -> V

public var body: V {
content(store.state, store.dispatch(action:))
}
}
22 changes: 22 additions & 0 deletions Sources/SwiftUIFlux/connector/StoreProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// File.swift
//
//
// Created by Thomas Ricouard on 23/07/2019.
//

import SwiftUI

public struct StoreProvider<S: FluxState, V: View>: View {
public let store: Store<S>
public let content: () -> V

public init(store: Store<S>, content: @escaping () -> V) {
self.store = store
self.content = content
}

public var body: some View {
content().environmentObject(store)
}
}
44 changes: 44 additions & 0 deletions Tests/SwiftUIFluxTests/SwiftUIFluxTests.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#if DEBUG
import XCTest
import SwiftUI
@testable import SwiftUIFlux

struct TestState: FluxState {
Expand All @@ -19,6 +20,31 @@ func testReducer(state: TestState, action: Action) -> TestState {
return state
}

struct HomeView: ConnectedView {
struct Props {
let count: Int
let onIncrementCount: () -> Void
}

func text(props: Props) -> String{
return "\(props.count)"
}

func map(state: TestState, dispatch: @escaping (Action) -> Void) -> Props {
return Props(count: state.count,
onIncrementCount: { dispatch(IncrementAction()) })
}

func body(props: Props) -> some View {
VStack {
Text(text(props: props))
Button(action: props.onIncrementCount) {
Text("Increment")
}
}
}
}

@available(iOS 13.0, *)
final class SwiftUIFluxTests: XCTestCase {
let store = Store<TestState>(reducer: testReducer, state: TestState())
Expand All @@ -30,6 +56,24 @@ final class SwiftUIFluxTests: XCTestCase {
XCTAssert(self.store.state.count == 1, "Reduced state increment is not valid")
}
}

func testViewProps() {
let view = StoreProvider(store: store) {
HomeView()
}
store.dispatch(action: IncrementAction())
DispatchQueue.main.async {
var props = view.content().map(state: self.store.state, dispatch: self.store.dispatch(action:))
XCTAssert(props.count == 1, "View state is not correct")
props.onIncrementCount()
DispatchQueue.main.async {
props = view.content().map(state: self.store.state, dispatch: self.store.dispatch(action:))
XCTAssert(props.count == 2, "View state is not correct")
}

}

}

static var allTests = [
("testExample", testStore),
Expand Down

0 comments on commit ccbafdd

Please sign in to comment.