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.
Related
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.
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.
The docs on viewControllerWithRestorationIdentifierPath:coder: say:
Your implementation of this method should create (or find) the
corresponding view controller object and return it... It is not always
necessary to create a new view controller object in your
implementation of this method. You can also return an existing view
controller object that was created by another means. For example, if
the view controller had already been loaded from a storyboard file,
you would return that object rather than create a new one. [My italics.]
This has always seemed like complete nonsense to me. This is a class method! We don't have any access to any instances at this moment — unless we create one. I'd be grateful if someone can explain to me how on earth a class method can find or know about "the view controller that has already been loaded from a storyboard file".
EDIT: To earn the bounty you must show me an actual case, from your own app, of the class method viewControllerWithRestorationIdentifierPath:coder: being used to "return an existing view controller object that was created by another means."
The most common example of this I can think of is any of the view controllers that are owned by the App Delegate. This is traditionally a tab bar controller or a navigation controller in traditional apps, but sometimes it can be something completely custom, which is when this functionality might be useful.
Since the UIApplication is pretty much a singleton and has one delegate, it means your App Delegate has global state, which makes it accessible from anywhere, including in class methods with: [[UIApplication sharedApplication] delegate].
Of course, any singleton is accessible from anywhere and a common pattern (but one I personally dislike) is to have a NavigationManager singleton which manages any global view controller transitions, so in this case you'd be able to access the existing instances as well.
I am struggling a little bit with the lifecycle of a UIViewController.
Basically, I have an application that consists of a UINavigationController, that has currently an UITableViewController as the root view controller.
The UITableView of this UITableViewController is updated with an asynchronious call to a web service.
Up to that point, everything works fine!
In general, I would like the UITableViewController to reload the data for the TableView every time the ViewController is shown.
That means, if I dismiss the app and start another app and then return to my application the data should be always reloaded from the web service. Basically, like the Twitter app as an example that updates the tweets each time when the app is restarted.
To achieve this, I placed the call to the web service in different places to test the behavior.
I tried, viewDidLoad, viewWillAppear & viewDidAppear but all of these methods are just called once when the viewController is created.
I did some research here in stackoverflow and there was the hint to use NSNotification applicationDidBecomeActive which does work but I am not sure if this is the proper solution if I add more ViewControllers to my app in future because I want the update of the viewController only when this controller is also shown/visible to the user.
Any suggestions on this issue are highly welcome!
Thanks a lot
Neo74
There is nothing wrong using applicationDidBecomeActive to update the information from the web services, and if you are planning to add more view controllers in the future, it is even better to have to web services logic outside of a single view controller.
What you shouldn't do is put the code to access your WS inside the application delegate, instead create a singleton class responsible of this.
I am a newbie summoned with lot of Questions. I am working with UITabBarController Window base application. Is it necessary to Delegate UITabBarController to AppDelegate. Its working fine for me with both delegating and not delegating.
i got some idea of delegate while working with UITableViewController. Please let me know what will happen with and without delgating UITabBarController.
Please suggest any Material or PDF or Book where i can get good idea with AppDelegate other than Apple docs.
You should read about the Model-View-Controller (MVC) paradigm and how delegates fit into it. A good source would be Apple's document on the matter, as well as most beginning iPhone Development Books. I would suggest this one, and a good starting place for MVC is here.
To answer your specific question, you want your AppDelegate to implement the UITabBarControllerDelegate protocol " when you want to augment the behavior of a tab bar. In particular, you can use it to determine whether specific tabs should be selected, to perform actions after a tab is selected, or to perform actions before or after the user customizes the order of the tabs. After implementing these methods in your custom object, you should then assign that object to the delegate property of the corresponding UITabBarController object." (From Apple's Class Document here.
In simplified terms, you use it when you want to do something custom/specific when a viewController is selected from the tab bar (– tabBarController:didSelectViewController:)or will be (– tabBarController:shouldSelectViewController:). It can also be used to help with customizing the viewControllers showing on the tab bar if you have a need for more than allowed to be displayed at once (using a "More..." or whatever tab).