What's the advantage of using delegate to transfer values between viewControllers? - ios

I have two viewControllers, vcA and vcB.
When I want to transfer values to vcA from vcB, I use delegate like this:
//vcB.swift
protocol TransferDelegate {
func transferValue(msg: String)
}
var delegate: TransferDelegate!
//vcA.swift
class vcA: UIViewController {
}
extension vcA: TransferDelegate {
func transferValue(msg: String){
//xxxxxxx
}
}
When vcA push vcB,then vcb.delegate = self
When vcB want to transfer values, then delegate.transferValue(msg)
My question is what if I transfer vcA's instance self to vcB instead of set delegate like this:
//vcB.swift
class vcB: UIViewController {
var vca: vcA!
func transferValues(msg: String) {
vca.msg = msg
}
}
//vcA.swift
vcb.vca = self
I have tried both two methods, both of them can work.
So why we use the first method rather than the second?
What's the difference between then or what's the advantage of the delegate?

The advantage of using a delegate is that vcB doesn't have (or need) any knowledge of the specific object type acting as the delegate. And this in turn makes it much simpler for vcB to be used by more than just vcA. It makes your classes less coupled and much more flexible.
Imagine of a UITableView only worked with a specific type of data source and delegate instead of allowing for anything that conforms to the proper protocols to act as a data source and delegate. This is no different.

protocol TransferDelegate {
func transferValue(msg: String)
}
The good thing is you can reuse this delegate method in other vcs.
Now you have vcA and vcB to do things. Maybe in the future you need something similar in other vcs.

See when you are using delegate you can only access that particular method of that particular viewController. But if you save the whole instance in the vc2 you can access or change any of the properties or functions of that class. Might be which you don't want to allow the vc2 to access.

Related

How to cause an update of model to trigger a update in another controller in swift

View Controller A has access to a model and can mutate the model. View Controller B displays the model. I want when View Controller A updates the model, a method to be triggered in View Controller B such that the view is updated. Is there a standard way to do this in Swift?
In the didset of this variable you can post a notification and the other controller can register for this notification. (We can do it more neatly depending on the requirement.)
If you make your model object an Objective-C object (#objc) then you can use KVO (key-value observering) to observe changes to it. If you do that then any object that's observing a property of the model will get notified when that property changes.
Here is one way you could do this.
Have whatever model you use, class or struct, implement a protocol that can be be used by view-controller B. view-controller B should not care what the model is, only that it conforms to the protocol it needs to extract the data it needs.
When view-controller A manipulates the model, post a notification with a reference to the model as an instance of the protocol, in the userInfo dictionary. view-controller B, subscribes to the notification and when triggered, extracts the reference from the userInfo and then gets the data it needs via the common protocol.
Another way to communicate between 2 view controllers is Protocol. Define a protocol, such as:
protocol ViewControllerAModelProtocol: class {
func viewControllerAModelDidUpdate()
}
In ViewControllerA, define a delegate type:
weak var updatedProtocol: viewControllerAModelDidUpdate?
When the model is updated and call:
updatedProtocol?.viewControllerAModelDidUpdate()
In ViewControllerB, implement the protocol:
class ViewControllerB: UIViewController, ViewControllerAModelProtocol {
func viewControllerAModelDidUpdate() {
// reloadData()
}
}
If ViewControllerB is presented or showed by ViewControllerA, set A's updatedProtocol to B:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let b = segue.destination as? ViewControllerAModelProtocol {
self.updatedProtocol = b
}
}

How to use delegate to performSegue in Swift

I have 3 classes :
Core that doesn't inherit from any class.
vcMain and vcIncomingFile which both inherit from UIViewController.
I have a segue from vcMain to vcIncomingFile.
How can I call the -performSegueWithIdentifier: method from the Core class,between vcMain and vcIncoming. I mean I want to have a method or delegate or anything else in Core class that can performSegue from vcMain to vcIncomingFile.
class Core {
func showIncomingVC(){ }
}
in showIncomingVC function, I want to performSegue between vcMain and vcIncomingFile.
Thanks
To implement the patterns properly we would need to know if your Core class is part of your model or if it is some kind of control flow mechanism.
If it is part of the model, it should know nothing about the view controllers but the view controllers are allowed to access it. In this case your view controllers could implement a delegate protocol of some sort and establish a call path from your Core class by assigning themselves as a delegate for some part of the Core functionality. The Core could then trigger any pre-defined behaviour in its delegates by calling the methods defined in the protocol.
For example,
If you define a protocol called FileEventDelegate with a method named fileReceived().
Then, add a member to your Core class called fileEventDelegate of type FileEventDelegate?
Whenever the Core class receives a file, it can call fileEventDelegate?.fileReceived(). The object instance, of whichever class implements the protocol, that registered itself as the delegate will handle it from there.
A unit testing class could also be the delegate and not even have nor need a segue to be performed.
Your Core class could even work without a delegate being set.
On the UI side, your VcMain class can implement the FileEventDelegate protocol by defining a fileReceived() function that call performSegueWithIdentifier(...). On viewLoaded() it can set itself as the fileEventDelegate of the Core class instance it is working with.
This keeps all model-to-visual logic in the viewController where it belongs.
If your Core class works in complete separation of the view controllers (meaning that the view controllers don't know how to access the instance(s) of Core), you may want to look into NSNotificationCenter and send notifications out in the universe for your viewControllers to pick-up asynchronously.
Core needs to be of UIViewController or some class that inherit UIViewController because performSegueWithIdentifier is a function of UIViewController
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/performSegueWithIdentifier:sender:
You can work with protocol instead. Create a protocol in your Core class with func showIncomingVC(){ } and use its delegate in your view class
Just do like this:
struct Segue {
let sourceVC: UIViewController
let sender: AnyObject
let identifier: String
}
protocol SegueProtocol: class {
var segue: Segue { get }
}
class Core {
weak var delegate: SegueProtocol?
func perform() {
guard let segue = delegate?.segue else {
return
}
segue.sourceVC.performSegueWithIdentifier(segue.identifier, sender: segue.sender)
}
}

Should I use a delegate method or UINotificationCenter

So right now I have a rootViewController which has a UIPageViewController as its only subview, rootViewController is basically a wrapper class for UIPageViewController. Now I want to disable scroll for the UIPageViewController from ViewControllerA (which is owned by rootViewController).
Right now I have implemented a delegate method (created by ViewControllerA) for the rootViewController which tells the UIPageViewController it has to stop scrolling depending on information given by ViewControllerA. But for the ViewControllerA to be able to call the delegate method implemented by rootViewController would require the rootViewController to be an instance variable of ViewControllerA.
Would this be the best practice? Would using NSNotificationCenter be a better choice? Or are none of these methods the best choice?
The best practice would be to use a delegate or a callback property:
final class ViewControllerA: UIViewController
{
var callback: (ViewControllerA -> ())?
func somethingHappened()
{
callback?(self)
}
}
If you're using this style, be sure to avoid a reference cycle:
let viewControllerA = ViewControllerA()
viewControllerA.callback = { [weak self] _ in ... }
If you're using a delegate, avoid a reference cycle by using weak var:
weak var delegate: ViewControllerADelegate?
These styles are preferable to NSNotificationCenter because they're more type-safe, readable, and flexible.
There are many ways to solve your problem. If you can set add a rootViewController variable to your ViewControllerA and able to set it during creation of ViewControllerA then that would be the quickest way to call it. I normally use the NSNotificationCenter when there are multiple threads involved. So if I have an async task running , like HTTP calls, and it would require the application to react accordingly depending on the outcome the using a NSNotificationCenter is a good choice as it would easily send the message app-wide and you just need to add an observer depending on the message.

Swift, how to tell a controller that another controller is its delegate

I'm learning Swift and I'm studying the delegation pattern.
I think I understand exactly what is delegation and how it works, but I have a question.
I have a situation where Controller A is the delegate for Controller B.
In controller B I define a delegate protocol.
In controller B I set a variable delegate (optional)
In controller B I send message when something happens to the delegate
Controller A must adopt method of my protocol to become a delegate
I cannot understand if every delegate controller (in this case A) listens for messages sent by controller B or If I have to tell to controller B that A is now his delegate.
I notice that someone use this code (in controller A)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Example" {
let navigationController = segue.destinationViewController as UINavigationController
let controller = navigationController.topViewController as AddItemViewController
controller.delegate = self
}
}
Is this the only way to tell a delegator who is his delegate?
I believe, you need to tell a deligator who is its delegate upon creation of that it. Now, the delegator can be created programatically or through storyboard. So, based on that you have two options, you can tell it who is its delegator programatically like you showed in the code or from IB.
The key here is upon creation. Let's me explain myself. Take the case of a UIView. Say, you want a Custom UIView object(CustomView). So, you drag and drop a UIView in your View Controller and in the identity inspector, you assign its class as of your CustomView's class. So, basically, as soon as the controller is created, your custom view will also be created. Now, you can either say it that the View Controller in which it is created is its delegate or You can go to the IB and connect the view's delegate to the View Controller.
Now, let's assume that you wanted the custom view to be created in your ViewController programatically. In that case, you would probably call the -initWithFrame: method to create the view and upon creation you tell that delegator that who is its delegate like-
myCustomView.delegate = self;
same goes with a View Controller.
controller.delegate = self;
So, basically to tell a delegator who is its delegate, you first need that delegator to be created. At least, that's what I think.
I think one of the best example of delegation is UITableView.
Whenever you want the control of various properties of a tableView e.g. rowHeight etc, you set your controller to be the delegate of your tableview. To set the delegate of your tableView you need to have tableView created obviously as pointed out by #natasha.
So in your case, you can set delegate of your delegator when you create it or when you find a need for the controller to be delegate of your delegator but you definitely need your delegator to be present to set its property.
You can set your controller as delegate at any time when you require control.
I'm sure you want your UIViewController to act like described, but here is a simpler example how to use the delegation pattern with custom classes:
protocol ControllerBDelegate: class {
func somethingHappendInControllerB(value: String)
/* not optional here and passes a value from B to A*/
/* forces you to implement the function */
}
class ControllerB {
var delegate: ControllerBDelegate?
private func someFunctionThatDoSomethingWhenThisControllerIsAlive() {
/* did some magic here and now I want to tell it to my delegate */
self.delegate?.somethingHappendInControllerB(value: "hey there, I'm a magician")
}
func doSomething() {
/* do something here */
self.someFunctionThatDoSomethingWhenThisControllerIsAlive()
/* call the function so the magic can really happen in this example */
}
}
class ControllerA: ControllerBDelegate {
let controllerB = ControllerB()
init() {
self.controllerB.delegate = self /* lets say we add here our delegate*/
self.controllerB.doSomething() /* tell your controller B to do something */
}
func somethingHappendInControllerB(value: String) {
print(value) /* should print "hey there, I'm a magician" */
}
}
I wrote the code from my mind and not testet it yet, but you should get the idea how to use such a pattern.

Pass data when dismiss modal viewController in swift

I'm trying to pass data from the modal ViewController to his source ViewController. I think I have to use delegation but it doesn't work.
protocol communicationControllerCamera{
func backFromCamera()
}
class Camera: UIViewController{
var delegate: communicationControllerCamera
init(){
self.delegate.backFromCamera()
}
}
class SceneBuilder: UIViewController, communicationControllerCamera{
func backFromCamera(){ // Never called
println("YEAHH")
}
}
The backFromCamera method it's not called. What did I do wrong?
You didn't set a delegate so it was empty when you tried to call backFromCamera().
Here's a simple working example you can test out. Notice the use of the optional type (?) for the delegate.
// Camera class
protocol communicationControllerCamera {
func backFromCamera()
}
class Camera: UIViewController {
var delegate: communicationControllerCamera? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.delegate?.backFromCamera()
}
}
// SceneBuilder class
class SceneBuilder: UIViewController, communicationControllerCamera {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
var myCamera = Camera()
myCamera.delegate = self
self.presentModalViewController(myCamera, animated: true)
}
func backFromCamera() {
println("Back from camera")
}
}
You can find all the information you need in Apple's Swift documentation.
Obviously the chosen answer is correct, but it didn't help me. I did successfully implement protocols though, so I wanted to provide my own explanation in case anyone is struggling with grasping the concept, like I was.
Protocol Code Is Written in Three Places:
Two ViewController Classes
The Protocol itself (code written outside of VC classes)
When I write my protocols, I put them in my "ToolBox" document and I still write comments to remind myself which VCs are doing what. Two examples:
So there is always:
The protocol code (shown above)
Code in a VC which initiates the action
Code in a VC which is delegated to carry out the action
1. The protocol code
See the image above for a reference. Essentially, the protocol code is just where you give the protocol a name and declare what functions you want to remotely call/delegate to. Name the protocol. Declare the names of the functions that can be called upon and declare their parameter types such as string, etc.
2. Code in a VC which initiates the action
This is the code that initiates the protocol. In this example, this is code from a table cell, which needs to delegate some work back to the main table VC. The first screenshot shows the creation of the delegate variable and the second screenshot is the actual use of that variable.
So the below code are table-cell buttons. They all need to trigger code outside of the cell VC, so they all trigger functions using the protocol I declared above.
3. Code in a VC which is delegated to carry out the action
Now the protocol is being called, but which VC answers the call? To answer that question, choose the VC and add the protocol name to the class declaration:
Lastly, you need the actual meat of the whole thing. Not the trigger, not the protocol itself, not the class declaration... but the actual function you want to call:
Hope This Helps
I don't know why protocols just wouldn't sink through my thick skull but they wouldn't. I hope this helps others like me!

Resources