swiftea icon indicating copy to clipboard operation
swiftea copied to clipboard

a simple example and some questions

Open mycroftcanner opened this issue 6 years ago • 1 comments

I tried implementing a super simple example to get started:

struct SmokeStatus: Codable {
  let isEnabled: Bool
}

enum SmokeMessage {
  case smokeIsEnabled(_ flag: Bool)
}

typealias SmokeMessageHandler = MessageHandler<SmokeEnvironment, ApplicationModel.Smoke, SmokeMessage>

private let smokeIsEnabled: SmokeMessageHandler = .reducer { state, message in
  guard
    case let .smokeIsEnabled(flag) = message
  else { return }

  state.isEnabled = flag
}

let smokeMessageHandler = smokeIsEnabled

typealias SmokeCommand = Command<SmokeEnvironment, SmokeMessage>

enum SmokeCommands {
  static let isEnabled = { (flag: Bool) -> SmokeCommand in
    SmokeCommand { environment, publish in
      publish(SmokeMessage.smokeIsEnabled(flag))
    }
  }
}

typealias SmokeEnvironment = SmokeServiceContainer

struct ApplicationModel: Codable {
  enum CodingKeys: String, CodingKey {
    case smoke
  }

  struct Smoke: Codable {
    enum CodingKeys: String, CodingKey {
      case isEnabled
    }
    var isEnabled: Bool = true
  }
}

This is my view

struct SmokeViewBody: View {
  var model: SmokeView.ViewModel

  var body: some View {
    Form {
      Section() {
        Toggle(isOn: model.isEnabled) {
          Text("Notification:")
        }
      }
    }
    .navigationBarTitle("Quote of the day")
  }
}

struct SmokeView: View {
  struct ViewModel {
    var isEnabled: Binding<Bool>

    private let dispatcher: ApplicationStore.Dispatcher

    init(smoke: ApplicationModel.Smoke, dispatcher: ApplicationStore.Dispatcher) {
      self.dispatcher = dispatcher

      self.isEnabled = Binding<Bool>(get: { () -> Bool in
        smoke.isEnabled
      }) { newValue in
        dispatcher.send(SmokeCommands.isEnabled(newValue))
      }
    }
  }

  var body: some View {
    ApplicationStateContainer(\.smoke) { smoke, dispatcher in
      SmokeViewBody(model: ViewModel(smoke: smoke, dispatcher: dispatcher))
    }
  }
}

It works 👍

2019-09-30 21:41:06.372950+0200 Rework[17769:1105701] smokeIsEnabled(true)
2019-09-30 21:41:08.522575+0200 Rework[17769:1105283] smokeIsEnabled(false)

My questions:

  1. is this the right way to bind a simple value like a flag
  2. Why is it so laggy? It takes a second for the toggle to be updated.
  3. Should I use a @State so that the toggle is updated right away? Wouldn’t that defeat the purpose?

mycroftcanner avatar Sep 30 '19 19:09 mycroftcanner

Take a look at @dynamicMemberLookup and @environmentObject...

   /// The value referenced by the binding. Assignments to the value
    /// will be immediately visible on reading (assuming the binding
    /// represents a mutable location), but the view changes they cause
    /// may be processed asynchronously to the assignment.

SwiftTEA should be able to do this.

I am really wondering now if a Redux or Elm like framework are really necessary for SwiftUI :

  • [x] The SwiftUI views are read only.
  • [x] Data flows one way either through @environmentObject which is really the application state or through a local state @state that you can pass to children.
  • [x] Access control to the state can be enforced by making properties read-only and forcing mutation using methods.
  • [x] SwiftUI takes care of the reducing and diffing

Am I missing something?

mycroftcanner avatar Oct 02 '19 18:10 mycroftcanner