All Projects → gilbox → Cloe

gilbox / Cloe

Licence: MIT license
Cloe is Redux on Combine for SwiftUI with excellent feng shui.

Programming Languages

swift
15916 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to Cloe

dashboard
iOS app + Today widget that monitors the status of my web apps. Updated for SwiftUI!
Stars: ✭ 41 (+41.38%)
Mutual labels:  swiftui, combine-framework
ReCombine
A Swift Redux library utilizing Apple's Combine Framework.
Stars: ✭ 46 (+58.62%)
Mutual labels:  swiftui, combine-framework
macos-snippets
Snip is a lightweight snippets manager app for macOS
Stars: ✭ 238 (+720.69%)
Mutual labels:  swiftui, combine-framework
DeTeXt
iOS app that detects LaTeX symbols from drawings. Built using PencilKit, SwiftUI, Combine and CoreML for iOS 14(or greater) and macOS 11(or greater).
Stars: ✭ 73 (+151.72%)
Mutual labels:  swiftui, combine-framework
SwiftUIKit
📱 UIKit code that is fun to write
Stars: ✭ 71 (+144.83%)
Mutual labels:  swiftui
SwiftUIAlarm
⏰ An example Alarm-like app using SwiftUI which is introduced in WWDC19
Stars: ✭ 36 (+24.14%)
Mutual labels:  swiftui
SwiftUI-App
This swiftUI Demo is very simple & easy to understand. This swiftUI demo includes On-boarding screens, login screen, forgot password screen, sign up screen, home & logout.
Stars: ✭ 175 (+503.45%)
Mutual labels:  swiftui
kmm
Rick & Morty Kotlin Multiplatform Mobile: Ktor, Sqldelight, Koin, Flow, MVI, SwiftUI, Compose
Stars: ✭ 52 (+79.31%)
Mutual labels:  swiftui
Combinative
UI event handling using Apple's combine framework.
Stars: ✭ 106 (+265.52%)
Mutual labels:  combine-framework
Scre
A lightweight screen recorder macOS application written in SwiftUI.
Stars: ✭ 53 (+82.76%)
Mutual labels:  swiftui
ScriptWidget
ScriptWidget is an iOS app that we can create widgets for iOS using JSX label style in JavaScript.
Stars: ✭ 137 (+372.41%)
Mutual labels:  swiftui
Redux
Manage iOS App state with Redux and Async/Await :)
Stars: ✭ 18 (-37.93%)
Mutual labels:  swiftui
SwiftUI-learning
SwiftUI视频教程配套代码(SwiftUI+SwiftUI 2.0+SwiftUI 3.0)+SwiftUI macOS+其他
Stars: ✭ 57 (+96.55%)
Mutual labels:  swiftui
hack-ios-dev
Hack iOS Development
Stars: ✭ 157 (+441.38%)
Mutual labels:  swiftui
Tasky
Tasky is a task management app made with SwiftUI.
Stars: ✭ 22 (-24.14%)
Mutual labels:  swiftui
LocalConsole
In-app console and debug tools for iOS developers
Stars: ✭ 595 (+1951.72%)
Mutual labels:  swiftui
CoreDataToSwiftUI
Rule based CRUD CoreData Frontends for SwiftUI
Stars: ✭ 18 (-37.93%)
Mutual labels:  swiftui
awesome-result-builders
A list of cool DSLs made with Swift 5.4’s @resultBuilder
Stars: ✭ 805 (+2675.86%)
Mutual labels:  swiftui
tca-swiftui-navigation-demo
Demo project that shows how to implement navigation in SwiftUI iOS application using Swift Composable Architecture
Stars: ✭ 75 (+158.62%)
Mutual labels:  swiftui
LunarCalendar
A lightweight macOS App for displaying calendar and time
Stars: ✭ 82 (+182.76%)
Mutual labels:  swiftui

Cloe

CI Status GitHub tag (latest SemVer) License

Cloe is Redux on Combine for SwiftUI with excellent feng shui.

Setup your store

struct AppState {
  var appName = "Demo App"
  var age = 6
  var names = ["hank", "cloe", "spike", "joffrey", "fido", "kahlil", "malik"]

  static let initialValue = AppState()
}

enum AppAction: Action {
  case growup
}

typealias AppStore = Store<AppReducer>

Setup your reducer

func appReducer(state: inout AppState, action: Action) {
  guard let action = action as? AppAction else { return }
  switch action {
  case .growup:
    state.age += 1
  }
}

Instantiate your Store

// Create a store with the publisher middleware
// this middleware allows us to use `PublisherAction`
// later to dispatch an async action.
let store = AppStore(
  reducer: appReducer,
  state: .initialValue,
  middlewares: [createPublisherMiddleware()])

// Inject the store with `.environmentObject()`.
// Alternatively we could inject it with `.environment()`
let contentView = ContentView().environmentObject(store)

// later...
    window.rootViewController = UIHostingController(rootView: contentView)

(Optionally) add some convenience extensions to the store

These extensions improve the ergonomics of working with the store. With the built-in dispatch function we would normally dispatch with store.dispatch(AppAction.growup). With this dispatch extension we can do store.dispatch(.growup) instead.

The subscript extension allows us to avoid using a closure with SwiftUI views. For example, a button can be implemented with: Button("Grow up", action: store[.growup]).

extension Store {
  func dispatch(_ action: AppAction) {
    dispatch(action as Action)
  }

  subscript(_ action: AppAction) -> (() -> Void) {
    { [weak self] in self?.dispatch(action as Action) }
  }
}

Connect your SwiftUI View to your store

This is an example of injecting state using a state selector. Here were define the state selector inside of the View, but it can be defined anywhere.

struct MyView: View {
  var index: Int

  // Define your derived state
  struct MyDerivedState: Equatable {
    var age: Int
    var name: String
  }

  // Inject your store
  @EnvironmentObject var store: AppStore

  // Connect to the store
  var body: some View {
    Connect(store: store, selector: selector, content: body)
  }

  // Render something using the selected state
  private func body(_ state: MyDerivedState) -> some View {
    Text("Hello \(state.name)!")
  }
  
  // Setup a state selector
  private func selector(_ state: AppState) -> MyDerivedState {
    .init(age: state.age, name: state.names[index])
  }
}

If you want to connect to the state of the store without defining a selector, use ConnectStore instead. Note that ConnectStore does not currently skip duplicate states the way that Connect does.

Dispatching a simple action

Here's how you can dispatch a simple action:

    Button("Grow up") { self.store.dispatch(AppAction.growup) }
    
    // ... or ...
    
    Button("Grow up", action: store[AppAction.growup])

Or with the optional Store extension mentioned above:

    Button("Grow up") { self.store.dispatch(.growup) }

    // ...or...

    Button("Grow up", action: store[.growup])

Dispatching an async action with the publisher middleware

Below is a simple example, read more about publisher middleware here.

    Button("Grow up") { self.store.dispatch(self.delayedGrowup) }
    
  //...

  private let delayedGrowup = PublisherAction<AppState> { dispatch, getState, cancellables in
    Just(())
      .delay(for: 2, scheduler: RunLoop.main)
      .sink { _ in
        dispatch(AppAction.growup)
      }
      .store(in: &cancellables)
  }

Tracking async task progress with publisher dispatcher

Publisher dispatcher documentation.

How is it different from ReSwift?

  • ReSwift is battle tested.
  • ReSwift is being used in real production apps.
  • Cloe uses Combine Publishers instead of a bespoke StoreSubscriber
  • Cloe's Middleware is simpler than ReSwift's Middleware but achieves the same level of flexibility.
  • Cloe's combineMiddleware function is simpler and easier-to-read.
  • Cloe provides a slick way to connect your SwiftUI views.
  • Cloe does not have a skip-repeats option for the main Store state, but when you Connect it to a SwiftUI component it always skips repeated states (subject to change).

Why does the Store object conform to ObservableObject?

You may have noticed that Cloe's Store class conforms to ObservableObject. However, the Store does not contain any @Published properties. This conformance is only added to make it easy to inject your store with .environmentObject(). However, since we don't expose any @Published vars don't expect a view with

@ObservedObject var store: AppStore

to automatically re-render when the store changes. This design is intentional so you can subscribe to more granular updates with Connect.

Example

To run the example project, clone this repo, and open iOS Example.xcworkspace from the iOS Example directory.

Requirements

  • iOS 13
  • macOS 10.15
  • watchOS 6
  • tvOS 13

Installation

Add this to your project using Swift Package Manager. In Xcode that is simply: File > Swift Packages > Add Package Dependency... and you're done. Alternative installations options are shown below for legacy projects.

CocoaPods

If you are already using CocoaPods, just add 'Cloe' to your Podfile then run pod install.

Carthage

If you are already using Carthage, just add to your Cartfile:

github "gilbox/Cloe" ~> 0.3.0

Then run carthage update to build the framework and drag the built Cloe.framework into your Xcode project.

License

Cloe is available under the MIT license. See the LICENSE file for more information.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].