Coder Social home page Coder Social logo

Comments (10)

luizmb avatar luizmb commented on May 14, 2024 1

Hey! Thanks for your message!
I just wrote a very simple example using RxSwiftRex and UIKit here, but it should be enough to demonstrate the general idea:
https://github.com/SwiftRex/UIKit-ReduxCount

Please confirm that I haven't forgotten to add any file, I always struggle with gitignore for new repos :)

ObservableViewModel is specific for SwiftUI, to satisfy ObservableObject protocol, which uses Combine's @Published property wrapper. It would make sense to project a RxSwift version of it, perhaps, for people migrating from UIKit to SwiftUI and using RxSwift, but I still haven't prioritized this as I see it as more or less an edge case.

For using pure UIKit, the Store Projection should be enough as give you input and output:

  • storeProjection.dispatch for input
  • storeProjection.subscribe(onNext:) for output

On top of that, you can use RxCocoa to make even better bindings, as store projection is itself an ObservableType already.

Store Projections are optional, you can use whole Store if you want, they both rely on very same protocol StoreType. But using Store Projections will help you to narrow the access of your ViewControllers, avoiding global state leaking in every view. Also helps with performance, because once you project a store, and the projected value is Equatable, by default it will only emit new values if projected state (like View State for example) changes, ignoring things that this view doesn't care about.

For example, in a huge system, if your state changes completely but in the end nothing on your view is affected by that change, because your view is more or less static or has only a single property that didn't happen to change, then your view won't be refreshed. This gives a huge performance boost, that could also be achieved by other ways, but by using store projections / view models you have a standard way of solving this.

Please let me know your thoughts about that and feel free to contribute with more complex samples, as you explore the framework.

I'll make my best to support you on that.

Best regards,

Luiz

from swiftrex.

MaikCL avatar MaikCL commented on May 14, 2024 1

Yes, I understood the idea. Basically each module will expose (in a simple way) a set of middleware that will "inject" into the main middleware of the App, which will react depending on the actions and states that they are able to recognize.

Anyway, I recognize that I still have a lot of practice, I have been practicing for 24 hours with the library after a failed step working with ReSwift and less than a month to convince myself to make the leap towards a Redux-type architecture.

You have been quite clarifying with everything, I will continue practicing it and if I have any questions, be sure that I will let you know. The documentation for the library (and Redux in general) is quite good. If you can continue complementing it it would be a super great plus.

My kudos to you.

from swiftrex.

MaikCL avatar MaikCL commented on May 14, 2024 1

I get it, I think my doubts have already been resolved. I thank you for the time, for my part I am more encouraged to use the library and apply it to my developments 😃

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024 1

Cool, share your experiences and questions, I'll do my best to support.
PRs are also welcome, as long as we avoid breaking changes :D

BR,

Luiz

from swiftrex.

MaikCL avatar MaikCL commented on May 14, 2024

Excellent, that example is very clarifying for what I need.

I am starting to integrate it into a large production app that we are starting to refactor it towards redux (accepting the risk that according to I see you still have it in development). For me, the ideal would be to refactor this app on SwiftUI Combine, but we still have to support users with iOS11, so we can only coexist with the RxSwfit UIKit combination. I think that is a great advantage of your library that allows you to maintain a compatibility with the old and not have to make big changes to change to Combine later.

What is not clear to me yet (but it must be due to lack of practice with the library yet), is if for example I have a middleware that executes logs and I configure it in the main Store, how to make an independent X module do a projection of this store, and you can add (or concatenate) a specific middleware of this module that contains the domain UseCases for this module.

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024

Hi,

About the development, I believe I won't have any breaking changes any longer. I'm introducing a new Middleware that is easier to use, but this is purely additive and it's currently only on develop branch. The rest of development will happen in separate repos. Only thing that bothers me currently, and could be a breaking change somehow is that SPM will fetch ReactiveSwift and RxSwift even if I use Combine-only... That way I'm considering to maybe split it in 4 repos. It's not a code breaking change but certainly it would require to update SPM packages (CocoaPods users should be fine, tho).

My company is about to release a mid-size app written with this library, and we are developing a huge one also using it. I have a dozen of personal apps myself, of all sizes, and there are other people using this library for some quite time already. The lack of practice could be a blocker for you, indeed, from my experience the learning curve on my team was a bit steep, but most of the difficulties were in the realm of Swift Generics, using the "lift" correctly is not always trivial.

You don't HAVE to use lifters/store projections at all, if all your view controllers use the global store, and all middlewares and reducers see the whole AppAction and AppState. You can start like that, then it's easy, and slowly narrow down the access of reducers, middlewares and views.

About having to support iOS 11, good news is that, depending on how you write your app, you can write most of your ViewModels, Reducers and even some middlewares that will be compatible with RxSwift and Combine. Then, later when you can, switching from one to another is really easy. Tonight I'll merge this RxSwift sample with Combine sample into the same workspace, and show you what I mean.

I'll also share some open source projects I'm writing using this library, so you can have an idea how that would fit your challenges. Most of them nowadays are SwiftUI + Combine, but that should be pretty similar to the UIKit example I posted earlier this morning.

About your last question, I'm not sure if I understood correctly. When splitting the app into modules what I recommend is having 1 middleware entry-point for the whole module, that is, even if this module has 3 or 4 middlewares, you make them internal and expose a public AnyMiddleware<ModuleAction, ModuleAction, ModuleState>. In that case it's also good to have a ModuleDependencies, that could be a tuple of your injected dependencies. I can talk about dependencies later, for now let's focus on the logs example.

My LoggerMiddleware is only available in the main target, together with other middlewares that don't need to be in modules, such as analytics, debuggers, crash reports, time machine (useful for AppStore screenshot automation and also replaying crashes or weird state). This LoggerMiddleware "speaks" AppAction and AppState, that are entities only available in the main target.

In the modules, the middlewares and reducers will speak a more narrow action and state entities. Maybe ModuleAction and ModuleState, maybe something even more narrow if you feel comfortable with "lift".

At some point in your module, you compose your Middlewares in a single public let moduleMiddleware: AnyMiddleware<ModuleAction, ModuleAction, ModuleState> = [mw1.lift(...toModuleAction/State...), md2, md3].reduce(ComposedMiddleware(), <>).eraseToAnyMiddleware().

Great, now in your main target you lift again from all the modules to the global types, such as let appMiddleware: AnyMiddleware<AppAction, AppAction, AppState> = [SomeModule.moduleMiddleware.lift(...toAppAction/State...), SomeOtherModule.moduleMiddleware.lift(...toAppAction/State...), ... ].reduce(ComposedMiddleware(), <>).eraseToAnyMiddleware().

That way, every module can run independently as an unit, and your main target is basically an AppDelegate/SceneDelegate where you compose everything together. Same for reducers.

I can give more examples later. Please let me know if I understood your question correctly.

BR,

Luiz

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024

This is WIP, but has enough to explain what I mean:
https://github.com/luizmb/SubtitlePlayer/tree/v2_SwiftRex

I can highlight some points in this project for you tonight. It's SwiftUI+Combine but it would not be that different in UIKit+RxSwift.

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024

Just to be clear, when I say I add the LoggerMiddleware only on the main target, it doesn't mean you CAN'T have it in other modules... The LoggerMiddleware is actually generic and accepts any action/state as long as the state is Equatable.
https://github.com/SwiftRex/LoggerMiddleware

But usually you don't have to add logger middleware anywhere else. Adding in the main target will be sufficient to monitor whatever action happens in any module, as all of them are routed through the same store. You will only have one store, and it will be in the main target. If your modules require a "sample app", you can have small stores in these sample apps too, but theses stores won't be used by your final app... The final app will have a single store that is in the main target and has a single instance, alive while your app is alive.

Store projections are not store, they behave like stores, but in the end they just point you to the real store (+ a couple of closures to lift types). Middlewares and Reducers can be tested independently and don't need a store for that, so you can have as many as you want in your modules and later just plug them into the main target. In fact, some middlewares are so generic that can be alone in their own repos:
https://github.com/SwiftRex/MultipeerMiddleware
https://github.com/SwiftRex/ReachabilityMiddleware

You can have a module UserLoginModule with a UserLoginView, UserLoginAction, UserLoginState, userReducer: Reducer<UserLoginAction, UserLoginState> and a middleware: AnyMiddleware<UserLoginAction, UserLoginAction, UserLoginState>. This is exposed as public and added to your main target's Store. :)

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024

Ok, this repository has the code for SwiftUI+Combine and UIKit+RxSwift examples in one. This is to demonstrate how much you can share between them, so any work done now in order to support iOS 11 can be an investment for whenever you're ready to drop 11 and 12.

I'm gonna close this ticket now, please feel free to reopen, or open a new one, in case of question.

from swiftrex.

luizmb avatar luizmb commented on May 14, 2024

I forgot to link the new repo. So smart! :D
https://github.com/SwiftRex/Examples-Counter

from swiftrex.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.