Open Source
CoreEngine
🌪️CoreEngine is a light-weighted and simple framework for a unidirectional data flow Swift architecture.
Scalabilty
Core is very Scailable. When you just have simple logic, you just conforms Core. Also whenever you got to handle Error and Side Effect you conforms AnyCore. If you want to use with RxSwift, you can exapnd Core to make what you want to
Side Effect & Error Handling
Not just simple core, but complex core is also supported. For example, Side Effect and Error handling. When it comes to those, you use AnyCore It is not very different from Core, since AnyCore also conforms. This method is defined in AnyCore and when you deal with side-effect generated publisher send into the function. Also you can handle every errors on the handleError(error: Error) function
func dispatch(effect: any Publisher) // deal with side effect
func handleError(error: Error) // deal with error handling
Example of AnyCore
let label = UILabel()
let increaseButton = UIButton()
let decreaseButton = UIButton()
var core: MainCore = .init()
func increaseButtonTapped() {
self.core.action(.increase)
}
func decreaseButtonTapped() {
self.core.action(.decrease)
}
func bind() {
core.$state.map(.count)
.sink { [weak self] count in
self?.label.text = "(count)"
}
.store(in: &subscription)
}
class MainCore: AnyCore {
var subscription: Set<AnyCancellable> = .init()
enum Action {
case increase
case decrease
case jump(Int)
case setNumber(Int)
}
struct State {
var count = 0
}
@Published var state: State = .init()
@Published var tenGap: Int = 10
private let sessionService = SessionService()
init {
dispatch(effect: sessionService.randomUInt$().map(Action.setNumber))
}
func reduce(state: State, action: Action) -> State {
var newState = state
switch action {
case .increase:
newState.count += 1
case .decrease:
newState.count -= 1
case let .jump(value):
newState.count += value
case let .setNumber(value):
newState.count = value
}
return newState
}
func handleError(error: Error) {
if let errpr = error as? MyError {
//handle
} else if let error = error as? APIError {
print(error)
}
}
func tenJumpAction() {
self.dispatch(effect: $tenGap.map(Action.jump))
}
}
class SessionService {
func randomUInt$() -> AnyPublisher<Int, Error> {
// blahblah
}
}
As I told, CoreEngine is very independent from Reactive framework. Therefore you can use with RxSwift.
Example + RxSwift
import Foundation
import CoreEngine
import RxSwift
protocol RxCore: Core {
var disposeBag: DisposeBag { get set }
func mutate(effect: Observable<Action>)
func handleError(error: Error)
}
extension RxCore {
public func mutate(effect: Observable<Action>) {
effect
.subscribe(onNext: { [weak self] in
if let self {
self.state = self.reduce(state: self.state, action: $0)
}
}, onError: { [weak self] in
self?.handleError(error: $0)
})
.disposed(by: disposeBag)
}
public func handleError(error: Error) { }
}
@propertyWrapper
class ObservableProperty<Element>: ObservableType {
var wrappedValue: Element {
didSet {
subject.onNext(wrappedValue)
}
}
private let subject: BehaviorSubject<Element>
init(wrappedValue: Element) {
self.wrappedValue = wrappedValue
self.subject = BehaviorSubject<Element>(value: wrappedValue)
}
var projectedValue: Observable<Element> {
return subject.asObservable()
}
func subscribe<Observer>(_ observer: Observer) -> Disposable where Observer : ObserverType, Element == Observer.Element {
return subject.subscribe(observer)
}
}