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

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.

Related

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/

Present View Controller without being on a View Controller - Swift / Objective-C

I'm creating an app that implements a Facebook and a Twitter service. In my view I have a button that toggles sharing on Facebook/Twitter or not. If Facebook/Twitter aren't connected, then the button will show "connect to".
When I click the button, a method in my controller (not my viewcontroller) will try to toggle the value because this controller tracks the state of my app. When I'm not connected to a social network my controller will notice and will call the correct service. This service will start a webview to provide the user credentials.
And now my question:
When I'm in my service and I need to provide credentials via a webview. When I want to show this webview I need to pass a View Controller that will handle the presenting. How do I provide a viewcontroller here?
Approaches I found:
Call the appdelegate singleton and get the current presenting
viewcontroller (I find this dirty but correct me if I'm wrong).
Since I'm injecting my service into my controller in
appdelegate.didFinishLaunchingWithOptions I could inject the UIWindow
of the appdelegate and ask for the current presenting viewcontroller
(Is almost the same as the first approach)
Create a protocol implemented by a viewcontroller. The service has a property that is equal to that protocol and in my app delegate inject the
viewcontroller into the service.
Have a property in your controller
that will be the presentingviewcontroller and let your controller
implement the approach #3 protocol. When a function of that protocol
is fired, the controller will handle it and use the
presentingviewcontroller property for the webview. I only need to
inject a viewcontroller into my controller in the appdelegate.
Don't implement anything view related in that service.
My current implementation is approach #3.
If some things are not clear, please let me know.
Any help is appreciated!
Thanks in advance!
I was hoping this question would of got more attention, I was interested to know how other people would handle this situation.
As you have already stated, there are a few ways to achieve what you need, but the answer depends on having knowledge of the application.
I would definitely try to keep business logic and UI as separate as possible, there are two methods that I can think of to do this, but they are pretty much the same thing.
Make the (non UI) controller responsible for the login check with a callback function, you can then leave the controller responsible for business logic and the calling ViewController responsible for rendering any UI as a result of that check.
As you suggested, using protocols, your controller could have a delegate that will be a ViewController that conforms to your protocol. Then usage would just be getting your shared instance, setting the delegate and calling the required fuctionality.
I would likely favor option 2 to be more Swift-like as it is a protocol orientated language.
If the controller is acting globally and accessed from anywhere within the application you could potentially end up duplicating a lot of code to make these checks, you could get around this by creating an extension of UIViewController which provides the functionality to interact with the controller.
I hope this helps, Would definitely be interesting to see the way other people would approach this.

What is the best way to show a message on Apple Watch independently of the active interface controller?

I would like to display a local message (generated by WatchKit code) on the Apple Watch. My problem is that the message may be triggered by a code segment outside of the currently active interface controller.
What is the best way to modally present a new interface controller independent of the currently active (top-most) interface controller?
One way would be to get a pointer to the currently active interface controller. But how to do this in an easy way?
One possible solution is to use a global property that holds a pointer to the currently active interface controller. It gets updated in the willActivate methods of all interface controllers.
Another solution would be to always pass the pointer to the currently active interface controller to all methods that may want to show a message.
Is there an easier way to get a pointer to the top-most interface controller?
Well, I'm not even convinced that the above approach is a good one. What if the top-most controller is dismissed shortly after presenting the interface controller with the message? Then the message is probably not shown very long or not at all.
Is it possible to modally present the message interface controller on top of everything independent of the currently active (top-most) interface controller?
Should I use local notifications? Is there something like UIAlertView?
What is the best and easiest way to solve this problem?
There is not any UIAlertView or WKAlertView in WatchKit. You must initialize a new view controller with a text view, and pass it the string you want to show.
You can use this method to do so:
Swift:
presentControllerWithName([Name] , context: [String])
Objective-C:
- presentControllerWithName:context:
To show a message independently of the active interface controller, it is possible to present an interface controller using presentControllerWithName:context: from the main interface controller (the one with the arrow in the storyboard). This works even if the main interface controller is not currently active.
For this solution to work, it is necessary that the method that calls presentControllerWithName:context: has a pointer to the main interface controller. This can be achieved by passing self from the main interface controller to other interface controllers and methods.
However, I sometimes want to show a message from an object deep down in the hierarchy. And I do not want to pass the pointer to the main interface controller to every method that possibly wants to display a message to the user.
So, my current solution is to store a pointer to the main interface controller in a global variable (using extern) and then presenting from this main interface controller.
Acknowledgements: I would like to thank "Schemetrical" and "Mike Swanson" for the comments that solved this problem.

Container View with multiple child views issue

I am trying to develop a Table View Controller(having a navigation controller),where rows are connected to multiple View Controllers (TextField,TextView,TableView,DatePicker,ImageView etc.).
So I design like this,if I click on any row,it should open one UIViewController having container view and then place the appropriate controller in the container.All the same type of tableview rows are using same View Controller as a child view of the container.
I am able to place proper view controller(example - 1.TextViewController for Text View
2. Table View Controller for Table view 3. DatePickerController for Date Picker.) in the container depend on their the row type.
But I am little bit confuse about how to pick the data from the child view when I click the done button(2nd screen right top). i.e for child Text Field I have to pick the input data whatever I type in the input box. For child Table view I hide the done button,so as soon as user select the data 'cellForRowAtIndexPath' should get fire and pass the seleted data.
How to do that data handleing? where to write that?
Is there any other way to design this?
As #Suhail mentioned the best way to do it, in general, when you want to pass data from a child view controller to the parent view controller, or in some cases from a controller to previous displayed controllers (that are still in the stack), is by using delegate pattern. You can implement delegate pattern with iOS protocols or with blocks. In my opinion, both approaches have their pros and cons, for that topic you'll have to do little more google search since this is not the place to discuss it.
Let's define some cases for your case(not all the cases):
You want to send data from ChildTableViewControler to Field controller (screen 3 to screen 2)
In this case, from what I understood, both controllers are embedded in a parent controller, so you'll have to set parent to be the delegate to the two child controllers. You have to create one or two protocols depending on your actions or data you want to send to the controllers. Create a property called delegate (you can choose your own name) on each of the children, implement the methods on the parent view controller, whenever you add one of the children on screen, set the delegate property to be the parent view controller. Now whenever you want to send data to the other child, you'll have to call the methods declared in your protocols. Remember, you have access from the parent to both children via childViewControllers propery.
Short version: One/Two protocols for children, parent implements the protocol(s) and responds to child action.
You want to send data from Filed to TableViewController (from screen 2 to screen 1)
In this case you'll declare a protocol in the parent view controller, which will be implemented by the TableViewController.Declare a delegate (or whatever name you like) property in the parent view controller. When you add the Filed controller on screen, you set the delegate property to be the TableViewController. Now you can communicate with the TableViewController from the Field controller via delegate property.
Short version: one protocol in parent view controller, TableViewController implements the protocol and responds to TableViewController actions.
You want to send data from ChildTableViewController to TableViewController (screen 3 to screen 1).
This is the same as case 2.
One of my rule when I send data from view controllers is something like this: if I want to send data forward (to the next screen that will be displayed) then I use property/methods. If I want to send data backwards(to previously displayed controllers) then I use delegate/blocks.
And my last advice, please check the delegate/blocks implementations and how to use them before you start implementing one of the above solutions. You can have lots of troubles if you implement them wrong, especially memory issues and random crashes.
A little bit off topic, if the reader of my answer is a 9gagger then "sorry for the long post, here's a potato"

UIViewController State Restoration - weak relationships

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.

Resources