UIViewController State Restoration - weak relationships - ios

With iOS 6 Apple added state restoration to UIViewController and related classes. This allows the application to save state when terminated and restore it when the user resumes the application.
Everything seems to work fine, however I have reached a weird scenario which doesn't want to fit into the pattern.
Let's assume we have two view controllers, ViewControllerOne and ViewControllerTwo, both of them store some arbitrary state that is restored successfully. Now let's imagine that ViewControllerOne has a delegate property and that ViewControllerTwo is that delegate (which is a common pattern for modal view controllers). Who is responsible for restoring this relationship? And how is it supposed to be stored/restored?
In my particular case no storyboards are involved, the restoration happens in code, via the restorationClass property. My first instinct was to try and restore the relationship whilst creating the view controller in the restorationClass, however as the restorationClass has no idea of other existing controllers it can't quite restore such a relationship.
Alternatively, if it is the view controller that declares the delegate property, that is supposed to restore the relationship, then how does it now of the controller instance that was restored in some other class?
In short, this seems like a poorly documented scenario and I was hoping someone could shed some light on it.

I would say, the task falls on the delegate view controller to set itself as such, just like you do it before pushing the other view controller.
On how you can accomplish this, you have several options.
You can store a weak reference to your view controllers in a globally accessible location (for example, the app delegate), and use these values in application:didDecodeRestorableStateWithCoder: to set the delegation - this is what this method is for in the API.
Alternatively, you could post a "hereIAmThisIsMe" notification (with self part of the user info) from the top view controller to which the delegate listens and sets itself as a delegate.

Related

Communication between View Controllers in iOS MVVM

I am working on a simple app to try out MVVM in iOS. I have only two screens in the app. Screen A displays content and the user can tap on settings button to pull up screen B. Screen B allows the user to change settings which affect the way screen A displays the content.
How should I communicate changes made in screen B to screen A so that screen A can re-display the content based on the setting changes? In MVC, I would use protocols/delegate to communicate changes made in ViewControllerB to ViewControllerA. Since view controllers have direct access to the models, I can just pass the updated model in the delegate call. In MVVM, would the communication happen between ViewModelB to ViewModelA or ViewControllerB to ViewControllerA(like MVC).
If the communication is happening between view controllers, what is ViewControllerB really passing to ViewControllerA? ViewModelA? If so, doesn't ViewControllerB knowing about ViewModelA defeat the purpose of reducing view controller responsibility/knowledge?
Thank you for taking time to read the question!
In a situation like this, I would engineer the data stream as follows:
Both ViewModelA and ViewModelB communicate with some entity from a model layer (the first M from MVVM). Frequently such entities are called "services" and contain persistence/network logic.
When the user opens ViewControllerB and changes some settings, ViewModelB makes respective changes to the model.
ViewModelA observes changes to the same model (via KVO, NotificationCenter, reactive publishers or a good old delegate pattern, whatever suits you best) and reacts to them by updating ViewControllerA if needed.
This way neither of your view models need to know about each other, and your model layer acts like a single source of truth for both of them.

Can you make a Swift Protocol that forces the view controller to fire specific custom NSNotifications?

This is a design pattern question.
I have a 'framework' I'm building, and depending on the current displayed ViewController the framework needs to inject a UIButton into that ViewControllers view.
I don't want the ViewControllers to 'know' explicitly about the 'framework', so I was initially using the delegate pattern but that required a 'over-seer' to ensure that when only specific view controllers were loaded, that they knew about the 'framework'.
However, it occurred to me if I could, via a protocol, ensure that all delete ViewControllers fired off the same two custom NSNotifications i.e. subscribeToRequestButton and unsubscribeForButton, then the 'framework' would listen out for those, and upon receiving them, have the view controller object passed to it (via the Notification) so that it can inject the button.
Thereby preventing the view controller from knowing about or having a reference to the 'framework'; it just knows, if it want's that button, to fire those two notifications, and ensure that it implements a method for when that button is touched.
Is this possible or is there a better approach?
The key problems are that
a) I cannot have the View Controllers that need the button know about the framework; as they never have the opportunity to have a reference to is passed to them by some manager class, and
b) The framework doesn't know about the view controllers existence until it receives a notification that is needs to inject a button into something.
EDIT:
I'll just use a singleton pattern with a few public accessor methods for passing data in or querying.

Keeping an updated model class and accessing it from multiple view controllers in Swift?

I am making an app with swift and xcode. In it I have a model class with user's information in it. I want to display some of that information with one view controller, update/change that information in another, and keep all of the views up-to-date with whatever is in the model class.
I was told using a singleton could work but is not recommended. Is there a better, more accepted way of accessing the model class from multiple view controllers without a segue between them (I am using a tabBarViewController)?
If you want to go "pure" that is follow Apple Best practices where a View Controller is never hard-coded to a global model (more of a pain than is worth, practically, in my opinion):
Subclass UITabBarController to contain the model instance as a stored property (perhaps private(set) is the best access level here, but however you want to expose it safely to other classes -- PUBLICLY EXPOSED AS A PROTOCOL TYPE ONLY OF COURSE.)
Don't forget to set the identity class of your Storyboard Tab VC to this new subclass otherwise your sexy code will not run.
In your (hopefully base class to all child tab VCs) View Controller class, hook into didMoveToParentViewController(parent: UIViewController). Gently downcast (as?) to the UITabBar subclass above, and extract the model, and copy the reference into your local stored property. It will happen exactly at the right time, just as you land into the soft arms of your parent VC.
Happiness!
You can try KVO mechanism. By which if you change a model variable's value that will reflect in another controller. For your reference
https://www.appcoda.com/understanding-key-value-observing-coding/

Best way to structure code

I have a UIView subclass acting as an internal "notification" system, that has a label inside that displays the notification text and animates down from the top of the view and back up. I need to create a system that will create a queue of these notifications (that can be added to from anywhere within my app), and display them one by one, but wondering how I should structure it.
Should I use a singleton that manages a queue of these UIViews? or should there be a UIViewController that has a queue? Or something else?
Thanks!
This is usually managed by a data persistence class. You want to separate data layer from view layer in order to maintain it properly. Implement the notification logic by creating a class that manages the notification data itself, in a queue, and just present said data in a view, in any viewcontroller that should display said notifications.
After you create the class that manages notification data, you can either:
Check inside viewDidLoad() if there is a notification in the queue. If true, present it.
Create a timer and check for the queue every couple of seconds/minutes
Get fancy and implement observer pattern so that every ViewController would subscribe to your notification manager class so that it notifies the VC that new data is available. When that happens, it will delegate through a protocol the presentation logic to the view controller (Preffered)
EDIT: If you go for #3 (and you probably should), remember to use weak for the delegate, as you don't want leaked VCs

How do I Instantiate Restored View Controllers with Managed Object Context from App Delegate?

Problem Statement
In my application, I instantiate a Managed Object Context (moc) in the applicationWillFinishLaunchingWithOptions method in my app delegate. During normal operation, I pass that same moc to all view controllers in my hierarchy via custom initWithMoc initialization methods I've set up for each view controller.
Everything works fine, but now I'm trying to use iOS6's built-in state restoration features.
When I get to the viewControllerWithRestorationIdentifierPath restoration method in the view controller, I'm not sure how to access the newly created moc from the app delegate.
Question
How do I point each view controller's moc to the same one from the app delegate?
According to Apple's iOS App Programming Guide: State Preservation and Restoration,
It is worth noting that if you specify a restoration class for a view
controller, UIKit does not try to find your view controller
implicitly.
and,
If you choose to use a restoration class, the implementation of your
viewControllerWithRestorationIdentifierPath:coder: method should
create a new instance of the class, perform some minimal
initialization, and return the resulting object.
you will be able to create instance of child view controller with your legacy initWithMOC: method for the State Preservation and Restoration API to use.
By doing it this way however you loose the benefit of 'it just works and all you need to do is specify the restoration identifier in your storyboard'.
Does anyone know a better way? The obvious way is to retrieve it from app delegate, but that is really a non-preferred way of doing it, so please do not recommend that. Besides all evil on a conceptual level, retrieving it from app delegate kills the benefits of using discardable nested contexts.
One way would be to register your NSPersistentContainer subclass or own Core Data stack object (adpopting the UIStateRestoring protocol) via UIApplication's registerObjectForStateRestoration and then encode and decode the pointer in the view controller subclass's restoration methods.

Resources