Git Product home page Git Product logo

Comments (8)

shoumikhin avatar shoumikhin commented on September 1, 2024 1

Yeah, I guess emitting an NSNotification would be the clearest way to update you singleton object. Let's close the issue for now, since there's probably not much of what any further Promises improvements could help you with at the moment. You're welcome to follow up, and we're happy to hear any feedback.

from promises.

 avatar commented on September 1, 2024

Hey Richard,
Sorry for the delayed response.

You can definitely have multiple subscribers for a single promise, so if you pass the promise to the "passive" view, that view can chain on the promise to determine when the network call has completed and then update accordingly. However, the main goal of promises is to replace completion handlers, so their intent is simple in that they can only be resolved once and cannot provide a new value in the future. Passing around a promise (or completion handler) to multiple views may not be ideal, in which case, it might be better to broadcast the update to multiple views using notifications (i.e default NSNotification or some sort of custom notification). The view that makes the network call can create a new promise that broadcasts the notification once the network call completes.

Hoping that helps answer your question, but please let me know if I am misunderstanding anything.

Thank you!

from promises.

richardtop avatar richardtop commented on September 1, 2024

Hi @temrich,

Thanks for your reply. It seems, that I should go for RxSwift and Observable for this task and not reinvent the wheel. Currently I'm using Promises as a substitute for completion handlers in the network calls and passing a promise around doesn't sounds like a good idea.

So, I agree, I'd go with a different solution for propagating the result to multiple subscribers.

from promises.

shoumikhin avatar shoumikhin commented on September 1, 2024

Hi Richard,

Just 2c. It’s hard to make any conclusions without seeing the code, but from what you’ve described there may be a chance you’re gonna face some design issues in future.

First, I suspect under the two “views”, one of which is using the service object and the other just passively observes, you really meant view controllers or some other sort of non-view components? Because UIViews shall not own any models or other business logic parts (like networking in you case) and interact with such directly, according to MVC and more advanced successors. So I assume you have two view controllers which are currently both on screen and one of them owns a view with a pull-to-refresh control, which triggers a callback where you initiate a network call, and the other view controller must also be updated when the network call completes.

Second, you’re saying the two views (or rather view controllers, as we clarified) are independent. I guess it’s not quite possible, because those views presumably belong to the same UIWindow (its root view controller), or rather some other parent view which is currently presented. So they both belong to a common view controller at some point.

Now, I believe the network call should be invoked by the controller (or another piece of business logic you may have, like navigation manager or something, depending on which design pattern you’ve adopted) which owns (or, al least, indirectly has references to) all the views you want to update. Thus, the controller which owns the pull-to-refresh control and gets notified when the latter is triggered by the user, can then pass that event to the component which owns both of your views that need to be updated, and also has a reference to the service object and will execute the network call, and once that is completed (in ‘then’ block, if you used Promises), it will tell all views/controllers to update with the new data.

Anyhow, that’s what I cal tell with the limited details provided. Just keep in mind the situation you’ve described seems pretty unusual, people rarely face, and I doubt RxSwift signals would address it well without significant architecture changes.

Would be glad to learn more about your progress and what you eventually come up with.

from promises.

richardtop avatar richardtop commented on September 1, 2024

Hi Anthony,

Thanks for the explanation and suggestions.
In fact, the diagram I posted here is quite simple and represents only a single edge case.

I'll describe the overall app structure, as well as the task I'm trying to solve.

Sorry for being unclear, by "Views" I meant "ViewControllers". I'm designing the app architecture similar to the one described in Advanced User Interfaces with Collection Views, Sample Code. I use a generic UIViewController subclass with a UICollectionView and custom layout. The behavior of the Controller fully depends on the DataSource object. Hence, I referred to the ViewController simply as a View. The DataSource uses the Service to make network calls.

I've attached a more detailed schema of the app, so the challenge could be seen in the context:

group

The app has a global NumberSelector, which is represented as a button in a custom UINavigationBar. To Synchronize multiple UINavigationControllers with currently selected number, I use a separate NumberSelectorState object. It is referenced by all the root UINavigationControllers (since they need to be able to change the number).

NumberSelector is a model object which uses the Observer pattern to propagate changes to all of its subscribers.

The subscribers are all the root UINavigationControllers and all the DataSources which fetch their data from the network.

DataSources might react on number change either by showing a different data (if it is already available), or by triggering a network call, if the data for the new number needs to be fetched.

Some endpoints, i.e. Endpoint1 contains the information for all the numbers, i.e.:

func getAllPhoneNumbers() -> Promise<[PhoneNumber]>

The other ones need to fetch the data from the network, they look like this:

func getDetailsForPhoneNumber(number: PhoneNumber) -> Promise<PhoneNumberDetailsResponse>

Currently, I see 2 options of syncing data from one network endpoint, e.g. Endpoint1 across multiple controllers:

  1. Make a common ancestor handle a network call and update the interested parties (this could be a controller or a service) - the option you proposed.
  2. Use Rx and subscribe all the relevant parties. If any of the parties triggers the update, all the others will be updated passively.

The only problem with approach 1 I see is that with the current app structure, the common ancestor is quite far away from the two controllers/DataSources. It will make the app quite inflexible, as there would be a lot of shared / non relevant code in a single entity.

Having said that, I'd like to hear your opinion/advice on how to link all the modules together, while still having the flexibility.

The approach described in Advanced User Interfaces with Collection Views is quite flexible, but as you can see, there are many ways to synchronize the DataSources.

Thanks in advance!

from promises.

shoumikhin avatar shoumikhin commented on September 1, 2024

Many thanks for the details, Richard!

From the first look, sounds like you have (or may need to have) a custom base navigation controller with a custom navigation bar with that button displaying the currently selected number. All other navigation controllers, which need that button to be presented, should inherit from such a base controller. The base navigation controller also subsribes to number-has-changed NSNotification and updates the button accordingly. Then, whenever the number changes due to a network call or somehow else, you also post a notification to inform all interested navigation controllers or other interested parties about the change. Thus, you get rid of the NumberSelectorState object and all the accompanying logic altogether.

Let me know if I’m missing any key details or need to clarify anything.

BTW, you may like to take a look at https://medium.com/square-corner-blog/caviar-ios-migrating-from-advancedcollectionview-to-pjfdatasource-81f8c2e4fcdf and https://github.com/Instagram/IGListKit, as alternatives to Apple’s approach you’ve been following.

from promises.

richardtop avatar richardtop commented on September 1, 2024

Hi, Anthony,

Thanks for reviewing my app architecture, to keep it structured, I'll reply with a numbered list:

  1. I already use the Custom Navigation Controller approach. However, I don't extend beyond the "CustomNavigationController" class, it's enough.

  2. I've already considered the NSNotification-based approach and decided not to go with it as it promoted implicit binding. However, I want to re-evaluate it, as virtually every DataSource in the app has to react on the number change and refresh the data in one way (a network call) or another (accessing a different local model).

  3. I wouldn't drop NumberSelectorState for now, just change the way it communicates changes (move to NSNotifications) - did you have some other approach in mind? The "State" becomes simply a model which emits notifications.

  4. Thanks for both suggestions, the Caviar article and IGListKit:
    4.1 Actually, I've read the Caviar article just before posting here. However, the landscape has changed significantly - now, UICollectionView supports AutoSizing cells (with a slight performance hit). Also, my app is different - it uses UICollectionView everywhere, so it's not an overkill like for the Caviar app. The other difference is that I'm not incorporating the Apple's sample code to my app, but rather using their approach and building my own micro-framework with UI components, which are reused across the whole app.
    4.2 I'm well aware of IGListKit, I might go with it if Apple's approach proves to be inflexible.

To sum up:

  1. I'll move to NSNotifications to propagate number changes
  2. So far, Apple's architecture approach works great, except this case. My app is very similar to the iTunes connect and what I've posted is the most complicated part of it.

I'll post some updates here as the app progresses.

from promises.

richardtop avatar richardtop commented on September 1, 2024

@shoumikhin OK, thanks a lot for the feedback and suggestions!

https://www.swiftbysundell.com/posts/observers-in-swift-part-1

This one is an interesting overview on the topic we discussed here. Might be relevant for the people who come to this issue later.

from promises.

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.