Skip to content

Instantly share code, notes, and snippets.

@atimca
Created May 23, 2020 10:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save atimca/c0c48c02088f9ce6543ab6328732b6b4 to your computer and use it in GitHub Desktop.
Save atimca/c0c48c02088f9ce6543ab6328732b6b4 to your computer and use it in GitHub Desktop.
Tiny implementation of Unidirectional Architecture with Query based SideEffects.
import Combine
import Foundation
func loadNewsTitles() -> AnyPublisher<[String], Never> {
["title1", "title2"]
.publisher
.delay(for: .microseconds(500), scheduler: DispatchQueue.main)
.collect()
.eraseToAnyPublisher()
}
struct SideEffects {
let loadNewTitles: () -> AnyPublisher<[String], Never>
func downloadNewTitles() -> AnyPublisher<Event, Never> {
loadNewTitles()
.map(Event.dataLoaded)
.eraseToAnyPublisher()
}
}
enum State: Equatable {
case initial
case loading
case loaded(data: [String])
}
enum Event {
case dataLoaded(data: [String])
case loadData
}
extension State {
var loadQuery: Bool {
guard case .loading = self else { return false }
return true
}
}
extension State {
static func reduce(state: State, event: Event) -> State {
var state = state
switch event {
case .dataLoaded(let data):
state = .loaded(data: data)
case .loadData:
state = .loading
}
return state
}
}
typealias Reducer = (State, Event) -> State
class Store {
@Published private(set) var state: State
private let reducer: Reducer
private let sideEffects: SideEffects
init(
initialState: State,
reducer: @escaping Reducer,
sideEffects: SideEffects
) {
self.state = initialState
self.reducer = reducer
self.sideEffects = sideEffects
}
func accept(event: Event) {
state = reducer(state, event)
}
func start() -> AnyCancellable {
$state
.map(\.loadQuery)
.removeDuplicates()
.filter { $0 == true }
.map { _ in () }
.flatMap(sideEffects.downloadNewTitles)
.sink(receiveValue: accept(event:))
}
}
let store = Store(
initialState: .initial,
reducer: State.reduce(state:event:),
sideEffects: SideEffects(
loadNewTitles: loadNewsTitles
)
)
var cancellables: [AnyCancellable] = []
store
.start()
.store(in: &cancellables)
store
.$state
.removeDuplicates()
.sink { state in print(state) }
.store(in: &cancellables)
store.accept(event: .loadData)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment