Protocols are not working in Swift (Getting nil) - ios

Refernce image Im new to protocols, I tried the following implementation to achieve the protocols, but im getting nil value for the protocol object
import UIKit
//MARK: step 1 Add Protocol here.
protocol MyDelegate: class {
func changeBackgroundColor(_ color: UIColor?)
}
class ViewController: UIViewController {
//MARK: step 2 Create a delegate property here.
weak var delegate: MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//MARK: step 3 Add the delegate method call here.
delegate?.changeBackgroundColor(UIColor.red)
}
}
Here I am getting delegate value nil and protocol not getting called.
here is the implementation
import UIKit
class HomeViewController: UIViewController, MyDelegate {
func changeBackgroundColor1(_ color: UIColor?) {
self.view.backgroundColor = color
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
callVC()
}
func callVC() {
let vc = ViewController()
vc.delegate = self
}
func changeBackgroundColor(_ color: UIColor?) {
self.view.backgroundColor = color
}

In short:
You have forgotten to set the delegate to the object that conforms the protocol.
Detailed Answer:
Protocol is an agreement between two objects so that you know a delegate will have some specified functions ready to be called. Let's explain with an example:
Suppose that you have two view controllers called A and B. You have defined your protocol in global (as you did in your code). Then you create a property called delegate in A which will hold a weak reference of any object conforms the protocol. In this case, this held object reference is B.
So you need to have a property in A like below:
weak var delegate: MyDelegate?
Then determine this delegate to be the reference of what you need. In our example, it's B. So you need to set it in B as below.
// Somewhere you have the reference of the object or where you initialize it.
instanceOfA.delegate = self
Finally you conform the protocol in B like:
extension B: MyProtocol {
func changeBackgroundColor(_ color: UIColor?) {
// some implementation goes here
}
}
There you are. Now, you can make sure that you have delegate object and protocol methods are getting called if you have completed steps above correctly.

The problem lies in your callVC method:
func callVC()
{
let vc = ViewController()
vc.delegate = self
}
You create an instance of your ViewController, but you are not doing anything with it. No methods, aside from initializer, will be called on that view controller because it's not a part of active navigation stack. Also, since it's just a local variable (not retained anywhere) it will be deallocated immediately after leaving the method scope.
What you need to do is to present the view controller somehow - either with a navigation controller, or as a child to the current view controller
func callVC()
{
let vc = ViewController()
vc.delegate = self
addChildViewController(vc)
view.addSubview(vc.view)
vc.didMove(toParentViewController: self)
}
or if you're using navigation controller
func callVC()
{
let vc = ViewController()
vc.delegate = self
navigationController?.pushViewController(vc, animated: true)
}

Hi Smart i think the problem is when you set the delegate
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
callVC()
}
func callVC() {
let vc = ViewController()
vc.delegate = self
}
Because you set the delegate for the class ViewController, but you don't present the viewController at all; you should set the delegate before presenting the ViewController, one way to do it is to present the ViewController and in function callVc add this
func callVC() {
let vc = ViewController()
vc.delegate = self
//Present the viewController with this
present(vc, animated: true, completion: nil)
}

Your optional delegate variable is nil. You need to set it first from the UIViewController you are segueing from. For example.
class OtherViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.performSegue(withIdentifier: "addHereTheSegueIdFromStoryBoard", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addHereTheSegueIdFromStoryBoard" {
guard let vc = segue.destination as? ViewController else {return}
vc.delegate = self
}
}
}

import UIKit
class ViewController: UIViewController {
weak var delegate: MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
if delegate != nil {
delegate?.changeBackgroundColor(UIColor.red)
}
}
}
Call like
class SecondViewController: UIViewController(), MyDelegate {
func callVC() {
let vc = ViewController()
vc.delegate = self
}
func changeBackgroundColor(_ color: UIColor?) {
//Code here
}
}

Related

Delegate function not calling

I have 2 UIViewControllers, ViewController, SecondViewController. I defined delegate function in VC, and using in Second VC. But delegate functions not calling in Second VC.
This is mu first VC code
import UIKit
//Step1:
protocol testDelegate {
func testFunction(string1: String, string2:String)
func math(a:Int, b:Int)
}
class ViewController: UIViewController {
//Step2:
var delegateVariable: testDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func moveToSecondVC(_ sender: Any) {
let nav = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
//Step3:
delegateVariable?.testFunction(string1: "String1", string2: "String2")
delegateVariable?.math(a:30, b:10)
self.navigationController?.pushViewController(nav, animated: true)
}
}
My second VC code
import UIKit
//Step4:
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
#IBAction func btn(_ sender: Any) {
//Step5:
let svc = ViewController()
svc.delegateVariable = self
}
//Step6:
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}
Here i'm just passing small amount of data for practice, but can any one please suggest some high level delegate example tutorial links for me.
This is why nothing is happening...
let svc = ViewController()
svc.delegateVariable = self
You are creating a NEW ViewController, not using the one that is actually in use.
It does not look like you are using the delegate pattern properly. Your ViewController should not be calling code on other view controllers.
SecondViewController should "do stuff" and then let ViewController know what it has done.
For the Math function you could just use a new class (not a view controller) and create and use this as needed. You do not need a ViewController for this.
An example of using a delegate might be something like:
protocol CreateProfileDelegate: class {
func didCreateProfile(profile: Profile?)
func didCancelCreateProfile()
}
class ViewController: UIViewController {
func showCreateProfile() {
let vc = CreateProfileViewController()
vc.delegate = self
present(vc, animated: true)
}
}
extension ViewController: CreateProfileDelegate {
func didCreateProfile(profile: Profile?) {
// show the profile?
}
func didCancelCreateProfile() {
// show an alert maybe?
}
}
This way the SecondViewController (CreateProfileViewController) basically tells the first that something has happened so that it can react to it.
in SecondViewController you are setting....
let svc = ViewController()
svc.delegateVariable = self
That just create an object of ViewController() class and then you set the delegate. So when the obj. of the scope is finished then the memory of the object will be increased automatically.
The flow should like below....
Create an object of the Viewcontroller in SecondViewController and set the delegate
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
Then push the view controller in to the navigation stack.
self.navigationController?.pushViewController(svc, animated: true)
Implement the delegate method of testDelegate in SecondViewController
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
}
EDIT
The final code of the SecondViewController Will be...
import UIKit
class SecondViewController: UIViewController , testDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func btn(_ sender: Any) {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ViewController") as! ViewController
vc.delegateVariable = self
self.navigationController?.pushViewController(svc, animated: true)
}
//MARK:- TestDelegate Methods
func testFunction(string1: String, string2: String) {
print(string1+string2)
}
func math(a:Int, b:Int) {
print(a+b)
print(a-b)
print(a*b)
}
}

swift delegate function is not called

I know the same question is asked many times. I read most of the answers from stack overflow and tried. But it did not help my problem.
I have two view controllers
protocol UpdateDataDelegate {
func loadData()
}
viewcontroller2 {
var delegate: UpdateDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
fun saveData() {
self.delegate?.loadData()
}
}
viewcontroller1 : UpdateDataDelegate {
var vc2 = viewcontroller2()
override func viewDidLoad() {
super.viewDidLoad()
vc2.delegate = self
}
func loadData() {
}
}
But function loadData() from viewcontroller1 is not called.
Since I don't have the complete code before me I can only assume that the delegate is not assumed properly.
If the delegate is not initialised properly it cannot pass value to the other viewController.
You can check delegate is properly initialised by:
if let delegate = delegate{
//Do your works here
}else{
print("The delegate is nil")
}
if the delegate is nil is printed in console, then the problem might be in the way the delegate was initialised
This might be because you are setting the delegate and opening an another instance of the viewController which was not assigned the delegate value.
In the code you provided I see that you are setting the delegate as
var vc2 = viewcontroller2()
vc2.delegate = self
But I cannot see the code that you used to move to the viewController2. Now we have to present this assigned viewController. Instead of using segue to move to the viewcontroller2 present this vc using the code below
present(vc2, animated: true, completion: nil)
You should place this according to your code logic.(where your segue is triggered)
Situation 2:
If you are using segue to move to the viewController2 then the delegate should be assigned in the prepareforSegue method as below
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc2 = segue.destination as? ViewController2{
vc2.delegate = self
}
}
let me know how it goes.
A simple playground for what you are trying to do, even if I have not clear what you are trying to achieve:
import UIKit
protocol UpdateDataDelegate: class {
func loadData()
}
class ViewController2: UIViewController {
weak var delegate: UpdateDataDelegate?
func saveData() {
self.delegate?.loadData()
}
}
class ViewController1: UIViewController {
}
extension ViewController1: UpdateDataDelegate {
func loadData() {
print("loadData called")
}
}
let viewController1 = ViewController1()
let viewController2 = ViewController2()
viewController2.delegate = viewController1
viewController2.saveData()
Few notes:
classes should be upper case. So, ViewController1 instead viewcontroller1
delegates should be weak otherwise you create reference cycles
class should be used for UpdateDataDelegate protocol otherwise compiler will complain since weak cannot be applied to class and class-bound protocol types
prefer extension to conform to protocols. It makes the code easy to read
The only thing I see missing in your code is call to saveData() of ViewController2 that will in turn call loadData() of ViewController1.
So just add:
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData() //Add this line to your code
}
You are good to go now :)
Edit:
protocol UpdateDataDelegate
{
func loadData()
}
class ViewController2: UIViewController
{
var delegate: UpdateDataDelegate?
func saveData()
{
self.delegate?.loadData()
}
}
class ViewController1: UIViewController, UpdateDataDelegate
{
var vc2 = ViewController2()
override func viewDidLoad()
{
super.viewDidLoad()
vc2.delegate = self
vc2.saveData()
}
func loadData()
{
print("Done")
}
}
I have used the above code and it is working fine for me. How are you executing it? I have used storyboard and used ViewController1 as the Initial View Controller.
I assume that you need to load data when your delegate has been set up. In this case you can use magic didSet:
weak var delegate: UpdateDataDelegate? {
didSet {
self.saveData()
}
}
So right after setting the delegate the needed method will be called.

Swift Delegate function not being executed

I have a UIPageViewcontroller containing three ViewControllers. One Viewcontroller is my ProfileViewcontroller. I have a button in my ProfileViewController, which should tell the UIPageViewCongtroller when pressed, to switch to the next Viewcontroller.
It's my first time implementing a delegate, but I just can't figure out why its not working.
UIPageViewController class:
class PageViewController: UIPageViewController, ProfileViewControllerDelegate {
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
self.newColoredViewController("Profile"),
self.newColoredViewController("Offer")]
}()
override func viewDidLoad() {
super.viewDidLoad()
dataSource = self
if orderedViewControllers.count >= 2 {
scrollToViewController(orderedViewControllers[1])
}
}
// MARK: ProfileViewControllerDelegate
func profileViewControllerDidTouchOffer(viewController:ProfileViewController, sender: AnyObject) {
scrollToNextViewController()
print("I'm pressing the offer Button")
}
ProfileViewController class:
protocol ProfileViewControllerDelegate : class {
func profileViewControllerDidTouchOffer(controller: ProfileViewController, sender: AnyObject)
}
class ProfileViewController: UIViewController {
weak var delegate: ProfileViewControllerDelegate?
#IBOutlet var profileImageView: SpringImageView!
#IBOutlet var offerButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func offerButtonTouchUpInside(sender: AnyObject) {
delegate?.profileViewControllerDidTouchOffer(self, sender: sender)
}
Answer
I updated the PageViewController class by changing the way I add the ViewControllers in orderderedViewControllers:
private(set) lazy var orderedViewControllers: [UIViewController] = {
// The view controllers will be shown in this order
let profileVC = UIStoryboard(name: "Main", bundle: nil) .instantiateViewControllerWithIdentifier("ProfileViewController") as! ProfileViewController
profileVC.delegate = self
return [self.newColoredViewController("Search"),
self.newColoredViewController("Menu"),
profileVC,
self.newColoredViewController("Offer")]
}()
Looks like you're using the InterfaceBuilder so I'm not sure how it's done there (probably in prepareForSegue), but a typical pattern is something like this:
let vc = ProfileViewController()
vc.delegate = self // Or whatever you want to act as the delegate
// Now show the view controller.
The key is that before using the view controller, you make sure it's delegate property is set to the thing that is the delegate and conforms to the protocol. Typically the calling controller would be the delegate.
EDIT: If you're using the UIPageViewController, then you should add the delegate to the viewController before passing it to setViewControllers https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/#//apple_ref/occ/instm/UIPageViewController/setViewControllers:direction:animated:completion:

Set receiving delegate in Swift without segue

I read a few posts about working with delegates in Swift, but mostly they advise to call the viewcontroller which receives the delegate with a segue. I am wondering how to do so without a segue, e.g. in a TabBar app. Here is the code for FirstViewController.swift
// FirstViewController.swift
import UIKit
protocol FirstViewControllerDelegate {
func didSendMessage(message: String)
}
class FirstViewController: UIViewController {
var delegate: FirstViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.didSendMessage("Hello from FirstViewController")
}
}
And here for SecondViewController.swift
// SecondViewController.swift
import UIKit
class SecondViewController: UIViewController, FirstViewControllerDelegate {
#IBOutlet weak var secondSubTitleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// how to set delegate here?
}
func didSendMessage(message: String) {
secondSubTitleLabel.text = message
}
}
How should I set the receiving delegate here?
this is typically not a scenario where a delegate would fit what you are trying to achieve. If you simply want to call some method in SecondViewController from FirstViewController you can get a reference by
if let vc = self.tabBarController!.viewControllers[1] as? SecondViewController {
vc.didSendMessage("hello")
}
or you might want to send a NSNotification instead to avoid the tight coupling which is introduced by the above code
In your AppDelegate:
NSNotificationCenter.defaultCenter().postNotification("ReceivedAppWatchData", object: self, userInfo: theData)
In any view controller where you want to recieve the data:
func viewDidLoad() {
...
// subscribe to notification
NSNotificationCenter.defaultCenter().addObserver(self, selector: "watchDataReceived:", name: "ReceivedAppWatchData",object: nil)
...
}
func watchDataReceived(notif: NSNotification) {
// handle data
}
deinit {
// unsubscribe to notifications
NSNotification.defaultCenter().removeObserver(self)
}
This way any view controller can access the data without knowing about each other.
Your tab bar controller is controlling the view controllers for each tab, so you should set the delegates in the tab bar controller.
class TabBarController: UITabBarController {
func viewDidLoad() {
super.viewDidLoad()
let firstVC = viewControllers[0] as! FirstViewController
let secondVC = viewControllers[1] as! SecondViewController
firstVC.delegate = secondVC
}
}
This code obviously has some type safety issues and is assuming that viewControllers[0] and viewControllers[1] are FirstViewController and SecondViewController respectively. Also, you should wait to call the delegate method after viewDidLoad in this example. The SecondViewController may or may not be loaded yet.

Delegate in Swift-language

I have two controllers and i need call up function the first controller to second controller:
In second controller I have created protocol and init delegate in class:
protocol testProtocol {
func testDelegate() // this function the first controllers
}
class SecondViewController: UIViewController {
var delegate: testProtocol?
....
}
#IBAction func testDelegateClicked(sender : AnyObject) {
delegate?.testDelegate()
}
First Controller
class ViewController: UIViewController, testProtocol {
var secondController: SecondViewController = SecondViewController()
override func viewDidLoad() {
super.viewDidLoad()
secondController.delegate = self
}
func testDelegate() {
println("Hello delegate")
}</pre>
But function not getting called
I am going to make an assumption you are using storyboards. If I am correct, then your issue is that your secondController, created in your First Controller, is not the actual one you are presenting. You will need to set secondController in your prepareForSegue:
Second Controller
Unchanged
First Controller
class ViewController: UIViewController, testProtocol {
// you will want to add the ? since this variable is now optional (i.e. can be nil)
var secondController: SecondViewController? // don't assign it a value yet
// ...
// implementation of the protocol
func testDelegate() {
println("Hello delegate")
}
// your prepare for segue
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
// get the controller that storyboard has instantiated and set it's delegate
secondController = segue!.destinationViewController as? SecondViewController
secondController!.delegate = self;
}
}

Resources