Open Source

CoreEngine

🌪️CoreEngine is a light-weighted and simple framework for a unidirectional data flow Swift architecture.

  • Inspired by ReactorKit, Redux but very light compared to existed architectures
  • Core is Reactive independent Framework which means you can expand whatever you want to import such as Combine, RxSwift.
  • 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)
      }
    }