In my app, I don't use Storyboard, and I have a coordinate class to start with the first view controller. And also I have a web socket manager class that connected to the coordinate class. So for example if the fifth View controller in hierarchy want to change something in the web socket class, I use event handlers or protocols to send data through each view controllers (for example here the forth VC get data and send it to the third, and then second and .. ) all the way down to the coordinate and it send it to the web socket manager.
I can use another option to use Notification and observers to send data directly from the fifth view controller in hierarchy directly to websocket manager manager and when I get the call back, use Notification and observers again to send data from web socket to the fifth view controller in hierarchy.
I don't know which way is the most effective way in term of use less CPU and RAM and also most reliable way. Could you please tell me which way is better here?
Thanks
IMHO I don’t think it’s a good use case for Notifications, as you are just passing an event from one object to another. Notifications are more useful when you need to broadcast an event that multiple objects will observe.
On the other hand, if you inject the event handler to all view controllers down the hierarchy, that would be a good solution if all of them depend on the Websocket manager. But if it’s only used by the fifth view controller down, perhaps it is not a good idea either, as you are injecting an irrelevant dependency to several objects and that’s not scalable.
If that is the case, I think your best choice here would be to have your Websocket Manager as a singleton and inject it directly into the view controller that requires it. I’m not a big fan of singletons, to be honest, but they are handy in cases like this. You just need to be careful of how you use it if it holds any state that can be changed by multiple objects concurrently.
Here is an example of how you could use this approach:
class WebsocketManager {
static let shared = WebsocketManager()
private init() {
}
}
class ViewController: UIViewController {
let websocketManager: WebsocketManager
init(websocketManager: WebsocketManager = .shared) {
self.websocketManager = websocketManager
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Hope it helps.
Related
So I'm using multiple delegates to pass trough data, but I don't know if it's the right way. I only want to maintain 1 main object to be sure that if a user exists/closes the app the edit data is saved when not in the VC1. By using the delegates now I'm sure the data is in the main object and all is well. Could you guys send me in the right direction and tell me if this is the way to go? Thanks in advance!
VC1:
Main object, holds all data that needs to be saved
Delegate for VC2
VC2:
Specific object overview from VC1
Delegate for VC3
Delegate function called -> PassDataToVC1(editsForSpecificObject)
VC3:
Edit's data from specific object. -> Gives
Delegate function called -> PassDataToVC2(edits)
Maybe even a 4th VC depending what I'm going to do.
What you're doing is basically correct for the architecture you have chosen:
If you have a rule that only VC1 knows the full data and can save it, then you need to pass changes in the data back to VC1.
If you have a rule that the way to pass the data back to VC1 is to use a protocol-and-delegate architecture, then you have to make VC1 the delegate for your protocol(s). There is nothing wrong with a single object (your VC1) being extended to adopt multiple protocols; that, indeed, is a standard Swift pattern.
However, I would argue that the reason this feels ugly to you is that those are not very good rules. I'll address them in reverse order:
Why delegation? In modern Swift, there are cleaner ways to pass data around. Modern app architectures are typically reactive, meaning that a handler of data is a publisher of the changes in that data, and the central store of the data subscribes to those publishers. Thus the data store can hear directly of any changes without the view controllers having to send those changes to anyone in particular.
Why a view controller? This is arguably the weakest point in your architecture. What business is it of a view controller to know anything about the data store and the mechanism for saving it? You should have a separate data store object retained by some securely persistent object such as the app delegate. This object stores and gatekeeps the data, and knows (or has a service helper who knows) how to persist it. You can use dependency injection to hand a reference to this object to every view controller — which also has the added benefit of making your code eminently testable. Even better, if you accompany this idea with the use of reactiveness that I outlined in the previous paragraph, you will have a clean "touch-free" architecture that just works, where every object does its own job and no other.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have been trying to learn how delegation with protocols work. I understood everything, but I can't think of when to use delegation other than when using table views and possibly scroll views.
In general, when is delegation used?
What is Delegation?
First of all, you should know that Delegation Pattern is not exclusive for iOS world:
In software engineering, the delegation pattern is a design pattern in
object-oriented programming that allows object composition to achieve
the same code reuse as inheritance.
But working with delegation in the iOS world is so common, I assume that you can see many of classes that provide a delegation/datasource for giving the ability to provide properties or behaviors for the used instance. It is one of main mechanisms of how objects talk to each other in CocoaTouch.
Alternatives:
However, delegation is not the only way to let objects talk to each other in iOS, you might want to know that there are:
NotificationCenter.
KVO (Key-Value Observing).
Completion handlers/Callbacks (using closures).
Target-Action.
Remark: in case if you are interested in comparing between them, you might want to check the following articles:
Communication Patterns.
When to Use Delegation, Notification, or Observation in iOS.
Delegates vs Observers.
When to use Delegation?
So, the question is: "So why should I use delegation instead of those options?"
I will try to make it simple; I would suggest the use of delegation when you have one to one relationship between two objects. Just to make it clearer, the goal of talking a little bit about the NotificationCenter is to try to make sense when delegations are used:
NotificationCenter represents one to many relationship; Simply, it works as: posting (notifying) a notification on a specific event and observing (get notified about) this notification -- it could be observed anywhere else; Logically, that's what one to many relationship means. It is a representation of the Observer Pattern.
How to Apply Delegation?
For the purpose of simplifying, I would mention it as steps:
Knowing the requirements: Each delegate has its own rules, listed in the delegate protocol which is a set of method signatures that you should implement for conforming this delegation.
Conforming for the delegation: it is simply letting your class to be a delegate, by marking it. For instance: class ViewController: UIViewController, UITableViewDelegate {}.
Connecting the delegate object: Marking your class to be a delegate is not enough, you need to make sure that the object you want to be confirmed by your class to give the required job to your class.
Implementing the requirements: Finally, your class have to implement all required methods listed in the delegate protocol.
For Example
Does it sounds a little confusing? What about a real-world example?
Consider the following scenario:
Imagine that you are building an application related to playing audios. Some of the viewControllers should have a view of an audio player. In the simplest case, we assume that it should have a play/pause button and another button for, let's say, showing a playlist somehow, regardless of how it may look like.
So far so good, the audio player view has its separated UIView class and .xib file; it should be added as a subview in any desired viewController.
Now, how can you add functionality to both of the buttons for each viewController? You might think: "Simply, I will add an IBAction in the view class and that's it", at first look, it might sound ok, but after re-thinking a little bit, you will realize that it will not be applicable if you are trying to handle the event of tapping the button at the controller layer; To make it clear, what if each viewController implemented different functionality when tapping the buttons in the audio player view? For example: tapping the playlist in "A" viewController will display a tableView, but tapping it in the "B" viewController will display a picker.
Well, let's apply Delegation to this issue:
The "#" comments represents the steps of "How to Apply Delegation?" section.
Audio Player View:
// # 1: here is the protocol for creating the delegation
protocol AudioPlayerDelegate: class {
func playPauseDidTap()
func playlistDidTap()
}
class AudioPlayerView: UIView {
//MARK:- IBOutlets
#IBOutlet weak private var btnPlayPause: UIButton!
#IBOutlet weak private var btnPlaylist: UIButton!
// MARK:- Delegate
weak var delegate: AudioPlayerDelegate?
// IBActions
#IBAction private func playPauseTapped(_ sender: AnyObject) {
delegate?.playPauseDidTap()
}
#IBAction private func playlistTapped(_ sender: AnyObject) {
delegate?.playlistDidTap()
}
}
View Controller:
class ViewController: UIViewController {
var audioPlayer: AudioPlayerView?
// MARK:- Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
audioPlayer = AudioPlayerView()
// # 3: the "AudioPlayerView" instance delegate will implemented by my class "ViewController"
audioPlayer?.delegate = self
}
}
// # 2: "ViewController" will implement "AudioPlayerDelegate":
extension ViewController: AudioPlayerDelegate {
// # 4: "ViewController" implements "AudioPlayerDelegate" requirments:
func playPauseDidTap() {
print("play/pause tapped!!")
}
func playlistDidTap() {
// note that is should do a different behavior in each viewController...
print("list tapped!!")
}
}
Quick Tip:
As one of the most popular examples of using delegation is Passing Data Back between View Controllers.
Delegation is used when you want to pass some information or state of object A to another object B. Usually object B is the object that created object A.
I will list some situations where you would use delegation.
Yes you're right. table views and scroll views use delegates because they want to tell whoever is interested (usuall your view controller) that "someone selected a row!" or "someone scrolled the scroll view!". Not only do scroll views and table views use delegates, UITextField and UIDatePicker and a lot of other views use delegates too!
View Controllers also have delegates. For example, UIImagePickerController. The reason why is roughly the same as above - because the UIImagePickerController wants to tell you messages like "an image has been selected!". Another example would be UIPopoverControllerDelegate. This delegate tells you things like "the popover has been dismissed!"
Other classes that use delegates include CLLocationManager. This delegate tells you things like "the user's location has been detected" or "failed to detect the user's location".
You can use delegation in your code when a certain view controller of yours wants to send messages to other view controllers. If it is a settings view controller, it might send messages like "the font size setting has been changed!" and the view controller that cares about the font size setting changing will know and change the font size of a label or something.
Delegate Method to Selectionimages
Create baseClass And Insert the following code
Create Another class then insert code
Delegation in IOS world and mostly in MVC (Model View Controller)
is a way for the View to talk to the Controller and it's called "blind communication"
and delegation means to give the " leading stick " to another object ( doesn't really care who is taking over but usually the Controller) to control over components that the view can not control on it's own (remember it's only a view) or doesn't own
to make it more simple ....
the controller can talk to a view but the view can not talk to the controller without Delegation
I'm using a library that manages the swiping between view controllers.
It seems like the only way that I can use a button in one of my view controller to navigate to the other is by calling a specific method from the view controller that manages the swiping feature.
It there any way to call a method in a different view controller?
Thank you
There are many ways to communicate between view controllers. One of the most common patterns for doing so on iOS is the delegate pattern. One view controller holds a reference to the other and the delegate conforms to a protocol. The protocol is a set of methods that the first view controller can call when specific events happen. I would try setting up a protocol and have one of your view controllers be a delegate to the other
If you take a look at the code that is hosted on GitHub you can see that the page controller is exposed from the EZSwipeController class. So given that you subclass EZSwipeController (or maintain a reference somewhere), you can now access the pageViewController property of it and scroll to a given page.
If you subclass:
class MyVC : EZSwipeController{
func buttonTapped(){ /* use self.pageViewController to "page" */ }
}
Weirdly, I have never personally worked with UIPageViewController, and as far as I can tell there is no easy way to scroll to a page in an easy fashion.
I haven't personally tried it (I usually validate my answers before posting), but you should be able to pull it off. If you don't have a reference to the view controller you need to get it (let me know if that is the core issue).
There is a question on SO that seems to enjoy some popularity regarding UIPageViewController paging:
UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source?
And from Apple docs:
I would also encourage you to look at the code inside of the EZSwipeController repo, where you can find a non-exposed method that does the scrolling:
#objc private func clickedLeftButton() {
let currentIndex = stackPageVC.indexOf(currentStackVC)!
datasource?.clickedLeftButtonFromPageIndex?(currentIndex)
let shouldDisableSwipe = datasource?.disableSwipingForLeftButtonAtPageIndex?(currentIndex) ?? false
if shouldDisableSwipe {
return
}
if currentStackVC == stackPageVC.first {
return
}
currentStackVC = stackPageVC[currentIndex - 1]
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
}
The "key" line is this one:
pageViewController.setViewControllers([currentStackVC], direction: UIPageViewControllerNavigationDirection.Reverse, animated: true, completion: nil)
I think EZSwipeController also exposes the view controllers contained in the pageViewController which is a property called stackVC, which is an array of the view controllers contained in the page view controller.
I assume with this knowledge you should be able to page to a given page, despite seeming a little "hacky" (IMHO the developers should have exposed paging logic from the get-go).
My advice for you after all this is simple:
Try to roll it yourself. This is not a huge undertaking and you maintain full control over what's accessible and how you want to work with it. The EZSwipeController class is quite small, so there is not a whole lot of code you'd have to write for your own solution. You could also go ahead and fork the repo and modify/use it to your liking.
I hope that helps. If you have any further questions or if something is unclear, I'd be ready to help you out.
Just to make it clear: I know MvvmCross is very flexible about where and how view models can be created. My question is more about proper separation of concerns, to simplify design of complex cross-platform applications.
Consider we have an app with customer list and customer details. On iPad and Surface the list and details are shown on the same page, but on smaller devices customer list and details for a selected customer are split between separate pages. So we have a PCL with CustomerListViewModel and CustomerDetailsViewModel. Now how should we manage view model lifetime from within the portable class library?
I originally did it using code in CustomerListViewModel implementation that looks like this:
public ICommand SelectCustomerCommand
{
get { return new MvxCommand(SelectCustomer); }
}
public void SelectCustomer()
{
if (formFactor == FormFactor.Phone)
{
ShowViewModel<CustomerDetailsViewModel>(new CustomerDetailsViewModel.NavObject(this.SelectedCustomer));
}
else
{
this.CustomerDetails = new CustomerDetailsViewModel(this.SelectedCustomer);
}
}
What is essential here is that we either call ShowViewModel that in turns asks a presenter to construct a CustomerDetailsViewModel object and render it in a new page or explicitly create an instance of CustomerDetailsViewModel and bind it to CustomerDetails.
After having seen episodes 32 and 42 of N+1 MvvmCross video series I am not quire sure this is the right way to do it. Of course it works, but should a view model care about instantiation details of other view model?
My second thought was to refactor this code and put this logic into a presenter, so I can simply write in CustomerListViewModel implementation:
public void SelectCustomer()
{
ShowViewModel<CustomerDetailsViewModel>(new CustomerDetailsViewModel.NavObject(this.SelectedCustomer));
}
... and presenter will do the rest inside the code triggered by ShowViewModel call. However, in the episode 42 it's shown how to control view model lifetime right from the associated view:
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
VisibleViewModel.IsVisible(false);
if (e.NavigationMode == NavigationMode.Back)
KillableViewModel.KillMe();
}
But if a view model lifetime is controlled by a presenter, shouldn't we try to place KillMe call inside presenter's logic? I know so small piece of code doesn't make much difference but couldn't this be an advantage to put it in presenter's class and reduce code-behind?
Definitely the ViewModel should not handle anything in regards to view (screen).
One quick idea I have is use a custom presenter which is able to create views based on the ShowViewModel<> requests.
The custom presenter is a view responsibility so you can test for screen orientation.
http://slodge.blogspot.co.uk/2013/06/presenter-roundup.html
For the second part of this question:
But if a view model lifetime is controlled by a presenter, shouldn't we try to place KillMe call inside presenter's logic? I know so small piece of code doesn't make much difference but couldn't this be an advantage to put it in presenter's class and reduce code-behind?
Currently view model presentation is orchestrated by the presenter - it gets ViewModelRequest objects and decides what to do with them.
However, it doesn't normally create the ViewModels - instead:
the presenter normally creates/shows a View
then the View creates (or locates) a ViewModel as part of the OnCreate, ViewDidLoad or OnNavigatedTo handling.
And so I don't think a ViewModel's lifetime is normally "controlled by a presenter" - instead I think a ViewModel is a "Model for a View" - so it's lifetime is "controlled by its view".
For the occasions where you need shutdown/dispose/killMe logic in your ViewModel, if you wanted to move that logic back inside the presenter - or into some other object - then you definitely could do so if you wanted to - it's your app and the app is king.
However, in these cases I suspect you would need the presenter to get some kind of notification from the View - as often the presenter doesn't know when Views are removed (when a modal is dismissed, when a Back button is pressed, when Android clears up stack views to save memory, etc).
As another way of thinking about this, if the presenter were renamed as INavigationService then what roles do you want that INavigationService to own within your app?
Here is something I just can't get to work....I can make the view controller talk to my custom objects just fine....but how do I send messages to the View Controller from my objects?
A message from myViewController to myObject would look like
[myObject doSomething].
What would the opposite message look like? Does it even make sense to send a message the other way?
Greatly appreciate your help!
You could pass a controller to a model, but often you want models to not depend on views or controllers.
To avoid that, make a protocol that the model wants to talk to and have the view controller implement it and have the model take an instance of the protocol, not the view controller as a property.
I often use NSNotificationCenter to broadcast updates from model objects to interested controllers. For a more tightly bound interaction, consider making a delegate protocol for the model object.
A notification is mostly one way, although the listener could access the model object that sent the notification. There can be any number of interested parties, including none if controllers come and go but the model is persistent.
A delegate is two way, but there can only be one delegate at a time. Usually a delegate is expected to outlive the object it is a delegate to. A delegate might be good for a phase of a model object lifespan that requires extra user input.
Notifications and delegates can be used simultaneously. As with UIApplication, the delegate is usually called before the notification is sent.
Why would you want your model to actively talk to anything in the first place? View controllers are the active managers of the app flow, and initiate communications to the model, not the other way around.
Can you say a more specific example of a situation where you would actually need to do this?
Like you suspect yourself, most of the time it doesn't make sense to send messages “the other way.” But if you really need to do that, an appropriate way to broadcast information “out” from models is notifications. You can have your model send notifications, and view controllers or any other objects can subscribe to those notifications if they care, but there is no tight coupling from model to other app pieces.