Comments (8)
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.
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.
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.
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.
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:
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:
- 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.
- 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.
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.
Hi, Anthony,
Thanks for reviewing my app architecture, to keep it structured, I'll reply with a numbered list:
-
I already use the Custom Navigation Controller approach. However, I don't extend beyond the "CustomNavigationController" class, it's enough.
-
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 everyDataSource
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). -
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. -
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 usesUICollectionView
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 ofIGListKit
, I might go with it if Apple's approach proves to be inflexible.
To sum up:
- I'll move to
NSNotifications
to propagate number changes - 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.
@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)
- Contextual type for closure argument list expects 1 argument, which cannot be implicitly ignored
- Objective-C Promises crashes in SwiftUI previews HOT 3
- ios 16 strings convertible to NSError HOT 1
- FBLPromises fails to load building macos flutter app HOT 2
- 'NSInvalidArgumentException', reason: '-[FBLPromise HTTPBody]: unrecognized selector sent to instance HOT 1
- Confused when retry a Promise variable
- type(of: value) is NSError.Type HOT 1
- Bitcode Issue on Xcode 13 when disable it. HOT 1
- Question regarding implementation HOT 3
- Ignore a certain Error and terminate Promise HOT 5
- Please release a new version of promises that includes latest changes for Firebase HOT 4
- Builds including PromisesSwift failing on CI
- Build fails for macOS with Xcode 14.3
- Create releases for new versions HOT 1
- The iOS deployment target 'IPHONEOS_DEPLOYMENT_TARGET' is set to 8.0, but the range of supported deployment target versions is 12.0 to 17.0.99.
- Privacy manifest for sensitive APIs HOT 6
- Version 2.4.0 not compiling HOT 15
- iOS build issue with 2.4.0 HOT 5
- PrivacyInfo.xcprivacy is not included in the framework output built by Carthage HOT 1
- FBLPromises.xcframework missing mac_catalyst support HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from promises.