sergdort / combinefeedback Goto Github PK
View Code? Open in Web Editor NEWUnidirectional reactive architecture using new Apple Combine framework https://developer.apple.com/documentation/combine
License: MIT License
Unidirectional reactive architecture using new Apple Combine framework https://developer.apple.com/documentation/combine
License: MIT License
Programmatic navigation has a dependency on NavigationView, but it has Safari-File-Picker behavior on MacOS which is not the most convenient one ๐. I tried to find workarounds for a while so maybe this will be helpful for other MacOS developers, which will use CombineFeedback ๐
switch
expressions, which can be handy for navigation, especially in MacOS, so I came to this solutionThere are example project, it showcase how to use stores independently and as a single store.
However in real life use case, usually one system need to communicate to each other:
for example:
We have an authentication_system and sign_in_system.
A persist_session_event in auth system will be triggered by signed_in_event from sign_in_system.
Can you share how to achieve that with the framework?
I am having trouble to follow the framework when it comes to the Feedback
context and although you can read the code, not sure how (some) feedbacks are connected to the event, it looks like there are various flavors of Feedback
, some are invoked every time state change and some are not making it difficult to understand when to use what.
Example:
// Having the below ViewModel
final class ViewModel: CombineFeedbackUI.Store<HostBroadcast.State, HostBroadcast.Event> {
init(initial: State = State()){
super.init(
initial: initial,
feedbacks: [
ViewModel.whenBroadcastFinished(),
ViewModel.whenSomethingElseHappened()
],
reducer: HostBroadcast.reducer
)
}
static func whenSomethingElseHappened() -> Feedback<State, Event> {
return Feedback.custom { state, consumer in
print("whenSomethingElseHappened")
return Empty().eraseToAnyPublisher().start()
}
}
static func whenBroadcastFinished() -> Feedback<State, Event> {
return Feedback(effects: { (state) -> AnyPublisher<Event, Never> in
print("\(state.status)")
guard state.status.isBroadcastFinished else {
print("#Broadcast not finished...")
return Empty().eraseToAnyPublisher()
}
print("#Broadcast finished...")
return Empty().eraseToAnyPublisher()
})
}
}
whenSomethingElseHappened
feedback will execute when the whole view is redrawn by the system; in my case every time a tab item selected change, but not when a button action in the view or onAppear
emits an event Button(action: context.action(for: .didFinishBroadcast))
, .onAppear { self.context.send(event: .shouldConnect) }
I'd appreciate it if I can further read somewhere how the Feedback lifecycle or connection to the events raised is working. I can also help with documentation.
In the lib itself, the Reducer<State, Event> is a typealias, like so:
public typealias Reducer<State, Event> = (inout State, Event) -> Void
In the Examples however, they use a struct, with an init, like so:
` public let reduce: (inout State, Event) -> Void
public init(reduce: @escaping (inout State, Event) -> Void) {
self.reduce = reduce
} etc...`
Where is the truth? :)
Thanks for the work on the library!
Following the examples in my own project, I realized that the Builder
helper protocol is not part of the library. Is that an oversight or should I just copy the set
method directly in my own project?
Trying to figure out if this is installed as a git submodule, clone and copy the files (I'm sure not), any help appreciated, a newbie in Swift and I'd expect something like cocoapods. Any keywords for me to Google is appreciated too :)
I noticed that the the when_() -> Feedback<State, Event>
static methods are defined both in the viewModel and in the corresponding enum in the examples. It's not necessary given that the view model can just access them from the enum type, ex:
...
feedback: [MoviesList.whenLoading()],
...
Can you please confirm that the current duplication is not the intended way?
I have a parent View with a TabView and a custom modifier to present a custom full modal view (doesn't exist in SwiftUI so custom work had to be done)
struct ContentView: View {
@ObservedObject private var tabData = MainTabBarData(initialIndex: 1, customItemIndex: 3)
var body: some View {
TabView(selection: $tabData.itemSelected){
// tab items with views and tags
}
.present($tabData.isCustomItemSelected, // Present the modal view when isCustomItemSelected @State is true
view: Widget(store: HostBroadcast.ViewModel(), content: BroadcastLiveView.init), // Using CombineFeedbackUI Widget passing the viewmodel and content view
// I need somehow to link the isPresented: $tabData.isCustomItemSelected so that I can toggle it from the BroadcastLiveView to close the modal, or if feasible signal from HostBroadcast.ViewModel the state change to MainTabBarData
style: .fade)
My BroadcastLiveView.init is following 0.7.0 tag source code examples.
typealias State = HostBroadcast.State
typealias Event = HostBroadcast.Event
@ObservedObject
var context: Context<State, Event>
init(context: Context<State, Event>) {
self.context = context
logInit(of: self)
}
I'd love to understand if this somehow can happen in ViewModel context, I have a Feedback
where I check if the button to close the view tapped but I lack understanding how to tell the MainTabBarData
ObservableObject that isCustomItemSelected
should be false.
I am following the SignIn example and I get the error in the static func reducer() -> Reducer<State, Event>
method.
Type 'Reducer<HostBroadcast.State, HostBroadcast.Event>' (aka '(inout HostBroadcast.State, HostBroadcast.Event) -> ()') has no member 'init'
Below is my HostBroadcastState.swift
implementation.
import Foundation
import CombineFeedback
import Combine
enum HostBroadcast {
struct State: Equatable {
var status = Status.idle
var error: NSError? {
switch status {
case .failed(let error):
return error
default:
return nil
}
}
enum Status: Equatable {
case idle
case connecting
case connected
case broadcasting
case failed(NSError)
}
}
enum Event {
case willConnect
case didConnect
case didFail(NSError)
case retry
}
static func reducer() -> Reducer<State, Event> {
.init { state, event in // ERROR HERE
switch event {
case .willConnect:
state.status = .connecting
case .didFail(let error):
state.status = .failed(error)
case .retry:
state.status = .connecting
case .didConnect:
state.status = .connected
}
}
}
}
And my HostBroadcastViewModel.swift
import Foundation
import Combine
import CombineFeedback
import CombineFeedbackUI
extension HostBroadcast {
final class HostBroadcastViewModel: Store<HostBroadcast.State, HostBroadcast.Event> {
init(initial: State = State()){
super.init(
initial: initial,
feedbacks: [
],
reducer: HostBroadcast.reducer()
)
}
}
}
Help is appreciated, thank you for the amazing work. Big fan!
Hi
I am trying use this nice framework , but I am struggling in problem how to manage state in multi screens application , can you provide a simple example of application with some screens , and in example with signIn :
static var feedback: Feedback<State, Event> {
return Feedback.combine(
whenChangingUserName(api: GithubAPI()),
whenSubmitting(api: GithubAPI())
)
}
why need it ?
Thanks in advance.
I think it would be convenient to export Combine to avoid duplicated import like so
import Combine
import CombineFeedback
(The first one seems redundant for me)
And @_exported import SwiftUI
for CombineFeedbackUI
Hey, this is a really interesting library, simple and nice to use, but I'm having hard times trying to figure out how to share data between multiple views.
What is the best approach to communicate state change and get feedbacks outside of a View/ViewModel?
Should it be done subscribing to the state: AnyPublisher<State, Never>
property of the view model? Or sending a Feedback
to the view model?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.