What's the difference between data source and delegate? - cocoa

I have a fundamental question related to Cocoa frameworks design patterns.
What's the difference between delegate and data source?
Both of them could use #protocols declaration, but some classes or frameworks are using delegate, and some others are using datasource.
All I can understand from UI/NSTableView is the delegate respond to UI-related events, while the datasource is purely related to the data. But, I don't know any data source implementations outside the UI classes of Cocoa.
Note:
The delegate I mentioned in this question is not always related to UI events.
The data source question has been answered.

The datasource supplies the data, the delegate supplies the behavior.
In MVC, datasource is in the model layer and the delegate is in the control layer.
Actually, on second thought, the datasource is usually controller that is lower down, closer to the model. I don't think I've ever used a model object as my datasource.

The delegate and datasource patterns are largely independent, and orthogonal:
The delegate pattern is very common in Cocoa and allows a delegate (any instance implementing the informal delegate protocol prior to OS X 10.6, or the formal delegate #protocol in 10.6 and later) to modify the behavior of an object instance. This pattern is often used instead of subclassing: instead of subclassing a class to change its behavior, you supply a delegate that responds to the appropriate methods. Classes that use delegates send messages to their delegate at contracted events. The API between class and delegate is defined by the class and is different for each class that uses the pattern, but the API generally consists of messages asking the delegate how to handle a particular event. One advantage of the delegate pattern over subclassing is that a class may implement multiple delegate protocols, allowing its instances to act as delegate for multiple classes. Similarly, an object instance can be the delegate for multiple other objects (hence most delegate APIs pass the object as the first argument to each message in the API). The delegate pattern is not as common in other UI frameworks (though Qt does use the delegate pattern in its Model/View framework), and is not the same as .Net/CLR delegates which are essentially typed function pointers.
The data source pattern is often used by NSView sub-classes in Cocoa that have complex state data such as NSBrowser, NSTableView, NSOutlineView, etc. The data source protocol defines an API that instances of these (and other) classes may use to get the data to display in the view. Although the NSController and Cocoa Bindings architectures have replaced many uses of the data source pattern, it's still common and very powerful. Like the delegate pattern described above, part of its power comes from an object being able to act as the data source for multiple data-source-using instances (and possibly even instances of multiple classes that have different data source protocols). The data source pattern is used commonly in other UI frameworks, such as Qt (in the Model/View framework where the model is analogous to the data source) and WPF/Silverlight (where the data source might be more closely analogous to the view model).

Suppose you had 3 tableviews. For dogs, cats and birds. Tapping on each cell would show a new screen with the enlarged photo of it.
To design this, you'll need to come up with 3 separate datasources for dogs, cats and birds. You basically need three arrays.
However you don't need 3 tableview delegates. Because the behavior of the tableviews are all the same. They all just take present a viewController and fill it with a UIImage. This is only true if you delegate is written in a generic way i.e. there's no dog, cat or bird specific code in the delegate.
Having that said you could abstract out the dog, cat, bird from the data source, but my answer was just a contrived example. Some custom objects are too complex to use the same structure, hence the need to have 3 datasources.
Old answer:
Before answering the question, you must better understand the delegation design pattern:
Let me begin with a question:
By default a TableView is like this:
How does a UITableView know how many cells to present? what to present in each cell?
By itself, it doesn't know.
It asks another class to inform it about the number of cells and what cell to return ( what cellimage, celltitle, cellsubtitle,etc.) values to itself. You usually see a tableView (delegating class) inside a ViewController (delegate class)
This concept of one class asking another is known as delegation!
Now that you know what Delegation is, to answer the actual question of the OP:
It's mostly a HUGE matter of semantic differences.
If you are only to use ( not to create your own protocol) foundation's delegates and datasources then it really doesn't matter for you. However if you intend to write custom protocols then understanding them would help you to write ( and with a higher importance read, refractor) code better.
From a developer's point of view, They both deal with the interaction between the delegat-ing class and delegate class.
Data Source
A data source is almost identical to a delegate. The difference is in
the relationship with the delegating object. Instead of being
delegated control of the user interface, a data source is delegated
control of data. The delegating object, typically a view object such
as a table view, holds a reference to its data source and occasionally
asks it for the data it should display. A data source, like a
delegate, must adopt a protocol and implement at minimum the required
methods of that protocol. Data sources are responsible for managing
the memory of the model objects they give to the delegating view.
In Layman's terms:
DataSource deals mostly with what and usually does it's stuff upon initialization.
Delegate deals mostly with how and feeds you some parameters to give a certain behavior ie if the user clicked this... what should happen? if they swiped...what should happen?
As an example for tableView:
DataSource
What does it have inside of it? What kind of cell am I presenting? cellForRowAtIndexPath.
What is the title of Section? titleForHeaderInSection
How many cells are they? numberOfRowsInSection
And therefore you usually return values. For delegates it's more common to be of type void.
Datasource methods
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell // return a cell ie UITableViewCell
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int // return a number ie an Int
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? // return the title ie a String
Delegate Methods
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
func tableView(tableView: UITableView, willBeginEditingRowAtIndexPath indexPath: NSIndexPath)
func tableView(tableView: UITableView, didEndEditingRowAtIndexPath indexPath: NSIndexPath)
I obviously chose selectively as some datasource methods don't return and some delegate method do return
Delegate
What should I do/what 'form of behavior' should I use after finishing the display of the footer, do you want me to pop an alert?didEndDisplayingFooterView
Am I going to have accessoryType that gives the cell some extra features? accessoryTypeForRowWithIndexPath

From my point of view, a DataSource is an object that doesn't know where the data is, and thus you should provide it. Such as telling an object how many items in a column.
A Delegate, which is a part that object shows to you, must be implemented by your class, because the object knows where the data is, but it does not know how to use it correctly.

To make it short:
Delegate relates to the UI and User actions against the cells and table.
common methods: willSelectRow, didSelectRow, willDisplay, heightForRow, willBeginEditingAt
Data Source deals with the editing, population and displaying of data on
the tableview.
common methods canEditRowAt, commit, titleForHeaderInSection, cellForRowAt, numberOfSections, sectionIndexTitles

Both are Protocol, now the main intension of Protocol is to keep an universal coding practice, or same coding practice for all(to my understanding). Suppose I am creating a tableView without a UITableViewDataSource & UITableViewDelegate, I would create the tableView such a way that you woud not. Thats where Protocol comes, apple created some set of rule or protocol and everybody have to follow that. Now DataSource & Delegate are obviously Protocol, seeing the name you could understand DataSource deals with something like numberOfRowsInSection, cellForRowAtIndexPath, numberOfSections of tableView where some kind of data is being taken/processed, and Delegates are didSelectRow, willSelectRow, heightForRow etc of tableView where its related with some kind of UI change/action. So its just naming convention nothing hypothetical to keep the task separate. As #kubi said earlier: The datasource supplies the data, the delegate supplies the behaviour.

Related

Using SwiftUI Coordinator with NSTableView in NSViewControllerRepresentable?

I have developed a macOS personal finance app in SwiftUI that uses NSTableViews and NSOutlineViews through the NSViewControllerRepresentable interface between SwiftUI and AppKit.
I have found that the AppKit view capabilities are far superior to anything I can create using SwiftUI lists plus they run faster and support TypeSelect. Passing data into the Appkit ViewControllers is straightforward but getting information out seems to be less so.
Apple developer documentation suggests that I should use a Coordinator class and I have tried to do so but to no avail. The delegated method that I need (func tableViewSelectionDidChange(_ notification: Notification)) fails to run (from inside my Coordinator class).
For the moment I am exchanging data and actions using NotificationCenter.default and that is working well but I am keen to use the 'official' method. I have found a macOS application that uses Coordinator successfully (at https://www.markusbodner.com/til/2021/02/08/multi-line-text-field-with-swiftui-on-macos/) but I cannot get my Coordinator class to work as a NSTableViewDelegate even though it builds and runs without errors (Xcode 12.4).
All I want to do for a start is to get an NSViewController that contains a NSTableView to pass the NSTableView.selectedRow into its parent SwiftUI view via a Coordinator, whenever the user selects a new row in the NSTableView. Can anyone help (with some sample code, if possible, please)?
I seem to have found the solution to my own problem. I had been allocating my NSTableViewDelegate to the Coordinator in the makeNSViewController function. However, it appears that my NSTableView is not being instantiated until the code that populates the contents of my NSTableView's NSTableViewDataSource has been run (in the updateNSViewController function). As a result there is no NSTableViewDelegate to allocate.
Moving tableVC.tableView?.delegate = context.coordinator into the updateNSViewController function, following the code that populates the contents of my NSTableViewDataSource, makes my NSTableViewDelegate work as intended and tableView.selectedRow values are now passed to my parent SwiftUI view via func tableViewSelectionDidChange(_ notification: Notification) successfully. Hurrah!
It may be useful to note that I am using Cocoa bindings in my NSTableView and have found that it is necessary to leave empty #IBAction func stubs (ctrl-dragged form Interface Builder as normal) in the ViewController (the File's Owner) and place copies of those funcs (populated with code, of course) in my Coordinator class to get their code to execute as intended.
The resulting overall code is much neater than my old code, which used notifications to pass actions and data between SwiftUI and AppKit - so the 'official' method looks to be best.

How to reference controllers in Swift (MVC)

I have a single window application. Currently I have one xib file (MainMenu.xib) and I have one .swift file (AppDelegate).
If I want to add controls to the UI and assign specific controllers to some of those UI components to to handle additional functionality in a separate file e.g. a NSTextView with a TextViewController - how would I obtain a reference to TextViewController, from within my AppDelegate?
Most tutorials stop short from this and assume that everybody will want to use #IBOutlets to access a controls' properties from the AppDelegate.
I know you can use segues (prepareForSegue) - but my application does not make use of storyboards, and I would like to understand the MVC implementation within cocoa a little better.
Any object can can have its own controller. The AppDelegate is not a holy grail, its just an object which implements methods in the UIApplicationDelegate protocol. Its not a universal switchboard for everything you might wish to connect.
Some of the tutorials do a starting Cocoa dev great disservice by using the AppDelegate as a quick and nasty place to put things. For a storyboard app this all that needs to be contained in the class which conforms to NSApplicationDelegate
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
}
i.e nothing.
A nominal pattern is one screen has one controller but even that isn't true when you use embedding patterns to control subviews within one window. However consider the simple Cocoa Application project template
The ViewController class is a subclass of NSViewController and is where you might be placing the IBOutlet to your NSTextView and some of the logic to do with interacting with it in that context.
import Cocoa
class ViewController: NSViewController,NSTextDelegate {
#IBOutlet var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
configureTextController()
}
var textController:TextFieldInteractionManager!
func configureTextController() {
//textcontroller can be local var , alternatively inject it into this VC
textController = TextFieldInteractionManager(textView)
}
}
If you have specific interactions that you want to do across the entire project you might want to place these in another class say TextFieldInteractionManager and make that the delegate of the text field. You would pass the text field to the manager class when you construct the view controller.
The hypothetical TextFieldInteractionManager might be created locally or injected in during the setup of the ViewController instance i.e prepareForSegue(segue: NSStoryboardSegue, sender sender: AnyObject?). The possibilities are varied and will depend on the scope of your app and how you wish to manage your references.
My commonly used pattern is to have a Root view controller which passes references down the to the next controller in the chain. YMMV
Dependancy Injection is a good pattern to know about.
Massive View Controller is a good anti-pattern to know about and avoid.
This is what I meant when i said go and get some domain knowledge. Its an expanding tree of stuff to learn about and why your question as its stands is not a good fit for SO.

How to manage windows in cocoa

I know the question is a bit generic but I guess my issue is generic as well.
I'm developing a small application in my free time and I decided to do it with Cocoa. It's nice, many things works almost automagically, but sometimes it's quite hard to understand how the framework works.
Lately I'm facing a new problem. I want to manage all the windows of the application from a single class, a front controller basically. I have a main menu and an "Import data" function. When I click it I want to show another window containing a table and call a method for updating the data. The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class? And more important, which should be the right way to do it? Should I extend the NSWindow class so that I can receive an Instance of NSWindow that can control the window containing the table (and then call the method)?
I may find several ways to overcome this issue, but I'd like to know which one is the best practice to use with cocoa.
PS: I know that there are tons of documentations files, but I need 2 lives to do everything I'd like to, so I thought I may use some help asking here :)
The problem is that this method is inside the class that implements the NSTableViewDataSource protocol.
How can I have a reference to that class?
These two sentences don't make sense, but I think I understand what you're getting at.
Instead of subclassing NSWindow, put your import window's controlling logic – including your NSTableViewDataSource methods – into a controller class. If the controller corresponds to a window, you can subclass NSWindowController, though you don't have to.
You can implement -importData: as an IBAction in your application delegate, then connect the menu item's selector to importData: on First Responder. That method should instantiate the import window controller and load the window from a nib.
In your import window controller's -awakeFromNib or -windowDidLoad method, call the method which updates the data.
Added:
Here's the pattern I'd suggest using in your app delegate:
#property (retain) ImportWindowController *importWC;
- (IBAction) showImportWindow:(id) sender {
if (!self.importWC)
self.importWC =
[[ImportWindowController alloc] initWithWindowNibName:#"ImportWindow"];
[self.importWC refreshData];
[self.importWC.window makeKeyAndOrderFront:sender];
}

Cocoa NSTextField Drag & Drop Requires Subclass... Really?

Until today, I've never had occasion to use anything other than an NSWindow itself as an NSDraggingDestination. When using a window as a one-size-fits-all drag destination, the NSWindow will pass those messages on to its delegate, allowing you to handle drops without subclassing NSWindow.
The docs say:
Although NSDraggingDestination is
declared as an informal protocol, the
NSWindow and NSView subclasses you
create to adopt the protocol need only
implement those methods that are
pertinent. (The NSWindow and NSView
classes provide private
implementations for all of the
methods.) Either a window object or
its delegate may implement these
methods; however, the delegate’s
implementation takes precedence if
there are implementations in both
places.
Today, I had a window with two NSTextFields on it, and I wanted them to have different drop behaviors, and I did not want to allow drops anywhere else in the window. The way I interpret the docs, it seems that I either have to subclass NSTextField, or make some giant spaghetti-conditional drop handlers on the window's delegate that hit-checks the draggingLocation against each view in order to select the different drop-area behaviors for each field.
The centralized NSWindow-delegate-based drop handler approach seems like it would be onerous in any case where you had more than a small handful of drop destination views. Likewise, the subclassing approach seems onerous regardless of the case, because now the drop handling code lives in a view class, so once you accept the drop you've got to come up with some way to marshal the dropped data back to the model. The bindings docs warn you off of trying to drive bindings by setting the UI value programmatically. So now you're stuck working your way back around that too.
So my question is: "Really!? Are those the only readily available options? Or am I missing something straightforward here?"
Thanks.
After a bit more research it appears that "Yes, really, your two options are to either subclass NSTextField or use your NSWindowDelegate to handle drops." I'll go further and make the claim that the better way of the two, for garden variety cases of, "I want multiple drop zones in a single window" is to use the NSWindowDelegate method with hit checks, since you avoid the issue of having your drop-handling code on the view side. I ended up with this draggingUpdated: method on my window delegate class:
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender
{
NSPasteboard *pboard = [sender draggingPasteboard];
NSDragOperation sourceDragMask = [sender draggingSourceOperationMask];
if ([pboard.types containsObject: NSFilenamesPboardType] && (sourceDragMask & NSDragOperationCopy))
{
NSView* hitView = [sender.draggingDestinationWindow.contentView hitTest: sender.draggingLocation];
if (hitView && (hitView == mSourceTextField || hitView == mDestTextField))
{
return NSDragOperationCopy;
}
}
return NSDragOperationNone;
}
Obviously there's more to the whole picture, but this hitTest:-based approach has worked for me so far. I suspect that this would be slightly more complex if one were working with a multi-NSCell based control like an NSTableView or NSOutlineView, but unsurprisingly, those have their own drag handling methods.
Hope this helps someone else.

Best way to handle multiple NSTableView(s)

What is considered the best way of handling multiple NSTableViews without using Cocoa Bindings?
In my app, I have two NSTableViews that are sufficiently closely related that I'm using the same object as the delegate and dataSource for both. The problem is both tableViews invoke the same methods. I currently discriminate between the two tableViews on the basis of NSControl -tag.
The deeper I get into this code, the uglier the use of -tag looks. I end up creating largely duplicate code to distinguish between the tableViews in each delegate/dataSource method. The code ends up being distinctly non-object oriented.
I could create a separate object to handle one or the other tableView, but the creation of said object would be a largely artificial construct just to provide a distinct delegate/dataSource.
Is everyone just using Cocoa Bindings now? I'm avoiding Bindings as I would like to hone my Cocoa skills on techniques that are transferrable between Mac OS and iPhone.
Every delegate/dataSource method for NSTableView passes the instance of NSTableView that's calling it as the first parameter (except for the ones that pass NSNotification objects, in which case the NSNotification's object is the table view instance). Some examples include:
- (int)numberOfRowsForTableView:(NSTableView*)aTableView;
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn*)aTableColumn row:(NSInteger)rowIndex
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
If you're using one controller object as a delegate/data source for multiple tables, you can just use that parameter to differentiate between them.
for the method :
- (void)tableViewSelectionDidChange:(NSNotification *)aNotification
you can use :
NSTableView *theTable = (NSTableView *)[aNotification object];
if(theTable==listeDesMots)
...
It sounds like you should be using a different delegate object for each view, but the same data source. In other words a single model for distinct view and controller objects.
I don't think this is an artificial distinction because the objects have sufficiently different purposes, but you want to use the same data. The bigger rule you are violating now is that each object should have a single purpose. Each objects' purpose could be to retreive and display the data in a specific way.
Good Luck!

Resources