I'm new to Swift and I'm having a hard time understanding the purpose of assigning self to a delegate. Part of the difficulty stems from the fact that delegate seems to be used in two different ways.
First is as means to send messages from one class to another when a specific event happens, almost like state management. Second is to enable "a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type," as stated in documentation. I have a feeling that these two are fundamentally the same and I'm just not getting it.
protocol PersonProtocol {
func getName() -> String
func getAge() -> Int
}
class Person {
var delegate: PersonProtocol?
func printName() {
if let del = delegate {
print(del.getName())
} else {
print("The delegate property is not set")
}
}
func printAge() {
if let del = delegate {
print(del.getAge())
} else {
print("The delegate property is not set")
}
}
}
class ViewController: UIViewController, PersonProtocol {
var person: Person!
override func viewDidLoad() {
person.delegate = self
person.printAge()
person.printName()
}
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
What is the purpose of person.delegate = self in this case? Isn't ViewController already required to conform to PersonProtocol without it?
I have a feeling that these two are fundamentally the same
The first is a special case of the second. "send messages from one class to another" is just a specific way of "handing off some of its responsibilities". The "messages" are the "responsibilities"
What is the purpose of person.delegate = self in this case?
Here, person delegates (i.e. hands off) some of its responsibilities to another object. It does this by sending messages to another object. First, it needs to identify which objects it can delegate these responsibilities to. This is achieved by requiring that its delegate conform to PersonProtocol, as PersonProtocol defines the messages that Person is going to send.
Next, person needs to know exactly which object it should send these messages to. This is what person.delegate = self does. Remember that person doesn't know anything about your ViewController until this point. Instead of = self, you could say:
person.delegate = SomeOtherClassThatConformsToPersonProtocol()
and person will send its messages to that object instead, and the methods in your ViewController won't be called.
Isn't ViewController already required to conform to PersonProtocol without it?
Correct, but without it, person doesn't know which object it should send its messages to, and as a result, the methods in your ViewController won't be called.
Note that the delegate property should be declared as weak to avoid retain cycles. When you do person.delegate = self, you get a retain cycle: self has a strong reference to person, person also has a strong reference to self via the delegate property.
If you notice inside your Person class, delegate is nil. If you don't execute person.delegate = self, delegate will remain nil.
In other words, assigning ViewController to person.delegate allows Person to identify who the delegate is (i.e., have a reference to ViewController), and that way you can successfully execute statements like delegate?.getName() or delegate?.getAge() from the Person class.
that means Person is not able to getName() and getAge() so Person class delegate that to other DataSource.
Lets say the your view controller has a data source class PersonDataSource which deal with API to get this information So
class PersonDataSource: PersonProtocol {
func getAge() -> Int {
print("view controller")
return 99
}
func getName() -> String {
return "Some name"
}
}
so the view controller will looks like this
class ViewController: UIViewController {
var person: Person!
var personDataSource = PersonDataSource()
override func viewDidLoad() {
person.delegate = personDataSource
person.printAge()
person.printName()
}
}
Related
I was going through Apple documentation and some tutorials where I learnt we can set observer which will be called if object if modified. But I have few doubts in my mind.
Here is a summary where a model is notified about the property changes:
Suppose there are 3 view controllers and they show listing of Foo models. Foo model has properties called id and title. My question is, is it feasible to get notified in others controllers that Foo model is modified which is having id 10 for example. Is that possible only if same instance of model is shared between 3 controllers, or we can achieve it although instances are different?
I am looking for a concrete solution, where a user like a feed (similar as Facebook) in one screen and if a feed with same id is in other controller, that controller should be notified that this feed is modified, refresh its UI. I have attached an image for clear idea.
I do not wish to go with delegate or notification pattern as it might create chaos, rather observer pattern will be more proper solution.
Here is an example of how you can achieve this.
Feed Model:
class Feed: NSObject {
var id: String
#objc dynamic var isLiked = false
init(id: String) {
self.id = id
}
}
Any property that you want to observe, mark it #objc dynamic, i.e. isLiked in Feed model.
class ListVC: UIViewController {
let feed = Feed(id: "1")
var observer: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func openDetailVC(_ sender: UIButton) {
if let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailVC") as? DetailVC {
self.observer = detailVC.feed.observe(\.isLiked, options: [.new], changeHandler: { (feed, change) in
if let newValue = change.newValue {
print(newValue)
//Reload the UI here...
}
})
self.navigationController?.pushViewController(detailVC, animated: true)
}
}
}
Next there is a ListVC that has a feed property.
Add an observer to feed in viewDidLoad(). Specify the property that you want to observe in feed i.e. isLiked. The closure in the observer will be called every time there is a change in isLiked property.
Now, DetailVC will be.
class DetailVC: UIViewController {
let feed = Feed(id: "1")
#IBAction func likeButtonPressed(_ sender: UIButton) {
self.feed.isLiked = !self.feed.isLiked
}
}
In the above code, I'm changing the value of isLiked property in feed whenever likeButton is pressed.
I have a protocol Vehicle and its extension like below:
protocol Vehicle {
func Drive()
}
extension Vehicle {
func Stop() {
print("iiiich...")
}
}
And I also have declaration like below for Stop Method
struct Car: Vehicle {
func Drive() {
print("Can Drive")
}
func Stop() {
print("yo stop")
}
}
let myCar = Car()
myCar.Drive()
myCar.Stop()
But its override the Stop Method
// Output
// Can Drive
// yo stop
And as per my requirement I need default method sometime and some time overridden method definition
Hey I got the answers that is to conform the protocol by the object call your default method rather than overriddedn, so we can call both defination as required
let honda: Vehicle = Car()
honda.Drive()
honda.Stop()
// Output
// Can Drive
// iiiich..
When we create a variable without type then this is static dispatch when a object conform a protocol only.
If you need the method declared in the protocol extension, just make the compiler think that the car is of type Vehicle:
let myCar = Car()
(myCar as Vehicle).Stop()
As already mentioned in the answers, the generic solution is to make sure that the instance that calls Stop() method is of type Vehicle (not Car). Nevertheless I would mention what's the logic behind it.
Personally, I think that there is a possibility to face this issue when it comes to work with the POP paradigm. Protocol extensions is a handy way the apply Polymorphism in our code, however it does leads to this "weird" behavior!
Static Dispatch:
Firstly, keep in mind that it is not a bug. In case of:
let honda: Vehicle = Car()
honda.Drive()
honda.Stop()
there is a manual cast to the honda as Vehicle, at this point the compiler will use static dispatch, which means that it would be recognizable which method should be called (Vehicle().Stop or Car().Stop) during the compile time. It selects the default implementation for Vehicle which is implemented in the extension, without the need of checking what is the concrete type.
Dynamic Dispatch:
In case of:
let myCar = Car()
myCar.Drive()
myCar.Stop()
nothing special goes here, it works exactly as expected. That's exactly the the meaning of dynamic dispatch, which leads to apply polymorphic operations during the run time.
To make it more clear, consider that you have another type that conforms to Vehicle protocol:
struct Bus: Vehicle {
func Drive() {
print("Bus Drive")
}
func Stop() {
print("Bus stop")
}
}
let myBus = Bus()
myCar.Drive()
myCar.Stop()
Obviously, the print("Bus stop") is the one which will be called, and actually that's the expected! The compiler is "smart" enough to recognize which method to be selected based on what is the concrete type (Bus().Stop).
Furthermore:
For better understanding of what's going on here, reviewing Understanding Swift Performance Apple session might be helpful.
You need a protocol with a default implementation that allows a struct param, that can do custom behaviors:
import UIKit
struct Car{
//Any properties
func drive(){
print("Yo Drive")
}
func stop(){
print("Yo Stop")
}
}
protocol Vehicle {
func drive(vehicle : Car?)
func stop(vehicle : Car?)
}
extension Vehicle where Self: UIViewController {
func drive(vehicle : Car? = nil) {
if (vehicle != nil){
vehicle?.drive()
}else{
print("drive default behavior")
}
}
func stop(vehicle : Car? = nil) {
if (vehicle != nil){
vehicle?.stop()
}else{
print("stop default behavior")
}
}
}
class ViewController : UIViewController, Vehicle {
func driving() {
drive() //will print drive default behavior
stop() //will print stop default behavior
let car = Car()
drive(vehicle: car) //will print yo drive!
stop(vehicle: car) //will print yo Stop!
}
override func viewDidLoad() {
driving()
}
}
I am using a UISearchController with GoogleMap Autocomplete View controller to send data to my view controllers.
I have a Tabbar controller and I want to send the data two my ViewController A and B.
I have done all the necessary jobs but only one ViewController gets notified when user has used the UISearchController.
I have tried setting the delegate of each tab to nil when I move to the other tab, For example if I move from ViewController A to B
I will set the delegate of A to nil and then set the delegate of B to itself.
I am kinda new to swift so Can anyone help me understand why isn't this working?
I have tried debugging my code to see is my delegate is nil and it wasn't .
Here is how i set and unset the delegate
func setDelegate() {
print("MapViewController is not nil")
print(resultsViewController?.delegate)
resultsViewController?.delegate = self
print(resultsViewController?.delegate)
}
func unSetDelegate() {
print("MapViewController is nil")
resultsViewController?.delegate = nil
}
You need an observer pattern, if you need that one class instance notify to a number of other instances you need make an array of delegates (called observers) and register and deregister from that notifier instance class
Further info Wikipedia Observer Pattern
example code
This is the protocol that must implement any observer class
protocol GeotificationsManagerObserver : NSObjectProtocol{
func nearestGeotificationsHasChanged(pgeotifications:[Geotification])
}
Notifier class
class GeotificationsManager: NSObject {
/**...*//code
fileprivate var observers : [GeotificationsManagerObserver] = []
/**...*//code
}
Observers methods
extension GeotificationsManager
{
func addGeotificationsManagerObserver(observer:GeotificationsManagerObserver)
{
for currentObs in self.observers {
if(observer.isEqual(currentObs))
{
//we don't want add again
return
}
}
self.observers.append(observer)
}
func removeGeotificationsManagerObserver(observer:GeotificationsManagerObserver)
{
var observerIndex = -1
for (index,currObserver) in self.observers.enumerated() {
if(observer.isEqual(currObserver))
{
observerIndex = index
break
}
}
if(observerIndex != -1)
{
self.observers.remove(at: observerIndex)
}
}
//here make the notification to all observers in observers array
func nearestsGeotificationsHasChanged()
{
for currObserver in self.observers {
currObserver.nearestGeotificationsHasChanged(pgeotifications: self.getNearesGeotifications())
}
}
}
Important
You must remove the observer once you don't need being notified if not you will have memory issue
Example: You can add a UIViewController as Observer in viewDidAppear and can be removed in viewDidDisappear
After doing loads of research I am still a little confused about how to use and implement delegates. I have tried writing my own, simplified example, to aid my understanding - however it does not working - meaning I must be a little lost.
//the underlying protocol
protocol myRules {
func sayName(name: String);
}
//the delegate that explains the protocols job
class myRulesDelegate: myRules {
func sayName(name: String){
print(name);
}
}
//the delegator that wants to use the delegate
class Person{
//the delegator telling which delegate to use
weak var delegate: myRulesDelegate!;
var myName: String!;
init(name: String){
self.myName = name;
}
func useDels(){
//using the delegate (this causes error)
delegate?.sayName(myName);
}
}
var obj = Person(name: "Tom");
obj.useDels();
I have read and watched so many tutorials but am still struggling. I no longer get error (cheers guys). But still get no output from sayName.
which demonstrates I must be misunderstanding how delegate patterns work.
I would really appreciate a corrected version of the code, with a simple explanation as to why it works, and why it is helpful.
I hope this helps others too. Cheers.
In Swift you omit the first parameter's external name, so your function call should be delegate.sayName("Tom")
Also, it is dangerous to use an implicitly unwrapped optional for your delegate property, as you have found. You should use a weak optional:
//the underlying protocol
protocol MyRulesDelegate: class {
func sayName(name: String)
}
//the delegator that wants to use the delegate
class Person {
//the delegator referencing the delegate to use
weak var delegate: MyRulesDelegate?
var myName: String
init(name: String){
self.myName = name
}
func useDels() {
//using the delegate
delegate?.sayName(myName)
}
}
Finally, your delegate must be an object, so you can't use a delegate in the way you have shown; you need to create another class that can set an instance of itself as the delegate
class SomeOtherClass: MyRulesDelegate {
var myPerson: Person
init() {
self.myPerson = Person(name:"Tom")
self.myPerson.delegate = self
}
func sayName(name: String) {
print("In the delegate function, the name is \(name)")
}
}
var something = SomeOtherClass()
something.myPerson.useDels()
Output:
In the delegate function, the name is Tom
I have two UITableViewController, the first one:
protocol FetchUserProfileData {
func getNumberOfRequests()
}
class ListEvents: UITableViewController{
var fetchInfo:FetchUserProfileData?
func getNumberOfRequests() -> Int{
return 12
}
and the UIViewController:
class UserProfileDetails:UIViewController, FetchUserProfileData {
var listEvents: UserListEvents?
func getNumberOfRequests(){
}
override func viewDidLoad(){
listEvents?.fetchInfo = self
print(listEvents?.getNumberOfRequests())
and this line: print(listEvents?.getNumberOfRequests()) gives me a nil value instead of 12... What's wrong here?
---- edit
Ok, now I see that listEvents is empty... So my question is how can I pass that data from ListEvents to UserProfileDetails?
In this code, listEvents is probably nil.
But, the way you use the protocol looks odd to me. I would expect:
getNumberOfRequests in the protocol to return Int
ListEvents should be implementing the protocol, not UserProfileDetails
The empty getNumberOfRequests() in UserProfileDetails should be deleted
You did not set listEvents. When you are using story boards then you should set the fetchInfo not earlier than in (overwriting) prepareForSegue. Google for examples, the web is full of them. When you segue programmatically then you can set the property not before you actually instanticated the new view controller. You are better of using listEvents!.fetchInfo = self because in that case you'll get an exception when listEvents is nil.
I made some change your code and this will pass data from ListEvents to UserProfileDetails.
protocol FetchUserProfileDelegate {
func getNumberOfRequests()->Int
}
class ListEvents: UITableViewController,FetchUserProfileDelegate{
var userProfile: UserProfileDetails?
override func viewDidLoad() {
userProfile = UserProfileDetails()
userProfile?.delegate = self
}
// MARK: FetchUserProfileDelegate
func getNumberOfRequests() -> Int{
return 12 // return as your target Int
}
}
class UserProfileDetails:UIViewController {
var delegate:FetchUserProfileDelegate?
override func viewDidLoad() {
if let _ = delegate{
let resultInt = delegate?.getNumberOfRequests() // get the Int form ListEvents
print(resultInt)
}
}
}
The idea of moving data from one controller to another is very common. Most of the time this is done using a segue. A controller can have a function called prepareForSegue. This function gets called before the transition happens. Inside the prepareForSegue function, the system gives you destination controller object. You take that object and set your data in it. When the transition happens, and your destination controller comes up, it already has the data you want to give to it.
Use Xcode and make a new project. Choose "Master-Detail Application". This will generate the code for you and it is a good example of how to pass data between controllers.