There's two views on a NavigationController stack:
EDIT ITEM-DETAILS VIEW (= basically a form)
SHOW ALL ITEMS IN A TABLE VIEW
I wonder if there are best practices for the task I have:
When the user taps "BACK" in the UINavigationController-bar (while being in view 1) the app should update the item on the server.
That's not so difficult, but the BACK-action leads to view 2, and 2 is not up-to date, because the update happened in the background and wasn't through before the GET-request for the table view data finished.
So in order to have view 2 always show accurate data, I have several options. All a bit annoying.. (for example having ViewController of view 2 talk to server on 1's behalf and update itself when completed, or having a "update happened" notification that triggers a reload, ...)
But.. what's a good best-practice for this case?
I think I would make a central place for the Items. Lets call it a ItemsStore. ItemsStore is a singleton that has the responsibility to have a set of the latest items and give access to the items. It also fires notifications if new data arrives, or old data is saved.
In this case:
View 2 adds the data to the store. The store notifies that there are
changes.
View 1 updates on the notification.
View 2 also asks the ItemsStore to
save the data to the server.
I would not give the responsibility of loading and saving to the controllers, it will get ugly and complex.
Related
I am currently working on an app using Xcode/swift 4 where I need a view (lets call it loggerView), which is not the main view, to be able to take data from the main view (or other views) in a manner that allows said data to be shown whenever loggerView is displayed (using a text view in this case). I understand that I can use a segue to present/push this data (text log msg) to the loggerView upon transition to the loggerView, but there are a couple of probs with that:
When using segues the data can only be pushed all at once through segue prepare when transitioning and not piecemeal, which I need.
When the user exits loggerView back to the primary (or other) view all the data in the loggerView is lost.
Because of (2) I would need the primary view to store all logging data related to the information I want displayed in the loggerView such that upon segueing to loggerView all the data can be provided each time. This is not ideal because I don't want the primary view to store this, I'm looking for a way to fire this data to loggerView in the same way a delegate might work back from loggerView->primaryView where it can be called at any time whilst loggerView is being shown.
I think the main prob here is that I am working with a parent->child view situ (using navigation controller) where every time we transition back from child to parent the child view (loggerView in this case) is removed from memory and so all data pertaining to loggerView is deleted, thus the need to segue everything each time loggerView is opened.
Is there a way to make more than one view permanent (memory wise) and then send data between primaryView and this other view on an ad-hoc basis rather like the way delegates might work in reverse (loggerView->primaryView)?
Summarizing our discussion in the comments:
Your objection to storing the child's data back in the parent is probably misplaced. Such behavior is normal and standard. If you insist on being different, you could keep the data in UserDefaults, as being both global and persistent, but that is usually regarded as a crutch.
In effect, your desire to persist the view controller merely in order to persist some data that it has fetched from the user is just wrong. Suppose a dialog pops up asking you to enter a name. You enter the name and tap OK, and the dialog vanishes. You do not believe that the dialog should persist behind the scenes just to hold the name, do you? No — what persists is the name. The dialog, and all the mechanism that enabled it to appear, was merely temporary in order to manage the user interface.
It might help to repeat "model view controller" (MVC) to yourself over and over as you work. The data collected from the user is model. If you want it to persist, persist it. But do not persist a view controller merely in order to do that. Think of view controllers as nimble; their purpose is to come and go, as the views that they control come and go. If you have data that needs to persist, it needs to be passed around, or put in a more persistent place. Model, view, controller. Three different things. Do not try to turn the controller into the model!
A user adds an item to their bag. As soon as the item is added to the bag, we make a network call to make sure that item is still available.The network call takes a few second to complete in the background.
When an item is added to the cart, it is stored in a singleton that can be accessed from anywhere in the app. Like this:
static let shared = Cart()
var products = [Product]()
When the network call returns and the product is unavailable, we remove it from the singleton. This causes an issue if the cart VC was opened during the network call because the table view needs to be reloaded. For that reason, we need to check if the VC that is visible is the cart VC and reload the table view. I would also like to check if the CartVC is in memory and reload the table view. Because if the cart VC is in memory and below another VC then it will also have bad data and when the user closes the VC on top, they will see bad data. How would I do that?
Before this is marked as a duplicate, I did check other posts and none of them work very well. There is also a lot of methods to do this and I would like to know which is best in swift 3.
You can use notifications to do it. Imagine the case where the item is not available anymore and the VC is open:
1 - Subscribe to a notification like "ItemNotAvailableNotification".
2 - The network call returned and the item is not available anymore.
3 - Post a notification "ItemNotAvailableNotification".
4 - In your VC handle the notification.
Also this approach allows you to handle the "bad data" in your "CartVC" and your "PreviousVC" where the user were lead to believe that the item was available.
When the cart is down in the stack make use of viewWillAppear and reload the table then. That way you don't reload the table more times than is needed. (If the cart is in the stack and four different network calls come back you only reload the table right before it appears rather than four times when it is hidden.) Table views are often reloaded in viewWillAppear specifically to deal with potentially stale data.
For the case when the cart is on screen your singleton can send a notification whenever a product comes back as unavailable and the cart can register for that notification in viewWillAppear and deregister in viewWillDisappear (or viewDidDisappear). The notification could either trigger a complete reload of the data or you could include which product isn't available and let the user know what happened (rather than something suddenly disappearing from the cart with no explanation).
This way the singleton doesn't need to know anything about the cart view controller allowing it to be more reusable.
In short, here is my problem:
I go from view controller A to VC B
Then from VC B to VC A.
Loop 1 and 2 for a number of times. All by pushing.
Now I edit VC A (e.g. Liking one of my post) and save. Then I go back by tapping on Back button. I want to see the changes in VC A (e.g. The number of likes increase 1 in my post)
That is the simple case when I only have 2 VCs. You can have many VCs in the loop, and you can may want to update other VCs not in the loop as well.
One example is in social network app like Instagram, where you can go to your profile, followers list, go to sb's profile, go to their follower list, go to your profile from there..so on and so on. Then at the end you make a like of one post, and you want to update view in Home tab, as well as all views in middle of the loop, while you tapping on Back button.
I know we can implement in 2 ways:
In each VC, in viewwillappear we check for update and then update. The disadvantage is that it will not work in offline mode. And it is bulky to check everywhere.
Use notification. One view having changes will notify other views to update. But you have to define by yourself which views will accept which types of notification, and having multiple notification triggered in your app is quite messy and hard to manage.
Are there any other ways? How are you doing in your app?
My question is more of asking about architect, not for coding sample, so please answer by giving a solution, an architecture and some short analysis of the result of it.
This question is for both ios and android.
My suggestion is
Save the number of likes and the post id in NSUserDefaults and check in each view controller while your post id is same as the value in NSUserDefaults the increment it by your count of like for the post in NSUserDefaults . But at some point of time you have to sync it with webservice .
I'm having a model that holds a bunch of items and sends an update notification when an item was modified. In this case there are two view controllers listening for this modification notification in order to update their state/(table-)views.
The problem is, that if one controller modifies an item it also receives the update notification and reloads it's content. But I don't want the controller who made the changes to be updated instantly, because it would interrupt the changes animation that the controller performs (because it knows what has changed).
Is there a good solution to only receive updates that weren't made by a specific controller? Or am I on the wrong path altogether?
Thanks!
You could try 2 different approaches:
First one is to use the "object" or " user info" information that you can add to a NSNotification object, send the view controller pointer and react on the notifications only when the object or user info is different than the object reacting to the notification.
The second approach is that you could remove your view controller from the NSNotificationCenter before performing the change, and adding it again afterwards.
I have an app that has a containerView that changes its view based on the clicking of one of three different tabs. Each tab contains different pieces of contract data.
It's now come the time for me to get ALL of the data from those tabs but I'm not sure the best method. Delegation is 1:1 and therefore I don't think would work as I can't be sure that each tab has been loaded. Same goes for the NotificationCenter as each has to register as an observer.
I've considered iterating through each and passing the message "view", this will verify each has been loaded, then firing off a Notification or while inside of each view calling a method to get me the data I need so that I'll end up with one large dictionary of values.
Any other ideas or commentary on my possible solution?
Let me know if more details are needed, this was a poor design from the start but I was required to implement it like this as the clients had approved the design BEFORE I started at this company and it took them several weeks to approve anything.
I've solved the issue of every subchild not being visible by calling [subviewName view] to assure that viewDidLoad was fired. Inside viewDidLoad I register for the notification, now I've assured that each view can create a dictionary and pass back it's information to the parent.