Unable to call another class method using delegate in swift - ios

I'm trying to call method of Class B from class A on the button tap event. But it does not work and below is my code.
// Viewcontroller
class ViewController: UIViewController {
#IBAction func btnClicked(_ sender: Any) {
var objA = A()
objA.delegate?.TestA()
}
}
// ClassA.swift
protocol TestA {
func TestA()
}
class A {
var delegate: TestA?
}
// ClassB.swift
class B : TestA {
func TestA() {
print(" Function A from b")
}
}
When tapping a button, function TestA() does not invoke.
I even tried the code below, but it also didn't work:
var objB = B()
var objA = A()
objA.delegate = objB

Because you instantiate instance of Class A using
var objA = A()
Clearly you haven't initialised delegate property in A because its optional its default value is nil
Now when you call
objA.delegate?.TestA()
delegate is nil hence function TestA will not be called
Suggestion
Always use camelCasing for declaring names of functions. So TestA() is incorrect rather use testA()
EDIT 1:
Tested this
#IBAction func btnClicked(_ sender: Any) {
let objA = A()
let objB = B()
objA.delegate = objB
objA.delegate?.TestA()
}
This is working fine what is the issue?

The objA.delegate is never assigned to an object, so it has an initial value of nil. The ? operator avoids calling a function on a nil object.

The answer by Sandeep Bhandari is right.
Some information for better understanding of Protocol and Delegates.
TestA is a protocol and an optional var delegate is defined in class A. This setup is right. The idea behind this setup is that any user of class A, in this case class B which conforms to protocol TestA gets a callback from class A. You need to call the delegate.testA() function from within class A.
The current implementation of ViewController is not at all benefiting from defining Protocol and Delegates.
To achieve proper usage, the class A cab be modified as follows:
protocol TestA {
func testA()
}
class A {
var delegate: TestA?
func process() {
// Do something and call delegate function to report it.
delegate?.testA()
}
}
And modify ViewController as follows (copied class B for completeness):
class ViewController: UIViewController {
#IBAction func btnClicked(_ sender: Any) {
var objA = A()
var objB = B()
objA.delegate = objB
objA.process()
}
}
// ClassB.swift
class B : TestA {
func TestA() {
print(" Function A from b")
}
}
Now function implemented in class B to conform to protocol TestA will be called when process() function on objA of class A is called.
This is better use of Protocol and Delegate. Hope this helps.

Related

Weak delegate becomes nil

In my app I'm using delegates, so that I can read the data when ever it's ready.
I'm calling a delegate from two classes. Here is my code
protocol MyDelegate: class {
func getData()
}
class MyDelegateCalss {
weak var delegate: MyDelegate?
func loadData() {
// doing some actions
if(self.delegate != nil){
self.delegate!.getData!()
}
}
}
In one class I'm loading this method in tableview numberOfSections delegate method.
class A: UIViewController, MyDelegate {
func somefunc(){
let mydelegatecls : MyDelegateCalss = MyDelegateCalss()
mydelegatecls.delegate = self
mydelegatecls.loadData()
}
func getData(){
// Doing some actions
}
}
This method I'm loading from another calss.
class B: UIViewController, MyDelegate {
open func testfunc(){
let mydelegatecls : MyDelegateCalss = MyDelegateCalss()
mydelegatecls.delegate = self
mydelegatecls.loadData()
}
func getData(){
// doing some other stuff
}
}
class C: UIViewController {
func testfunc(){
let b : B = B()
b.testfunc()
}
}
Here from class A my delegate is working fine. and I'm able to see getData method is calling .
from Class B, the delegate becomes nil and unable to see getData method is called
If I make the delegate reference its working fine. But that will cause memory leak.
How can handle this case ?
Your delegate var is declared as weak. If nothing keep a strong reference on the object you assign as the delegate (implementing MyDelegate), your delegate will pass to nil as soon as the object is released (eg. the end of the scope where you instantiate it).
Some good read: https://cocoacasts.com/how-to-break-a-strong-reference-cycle/

Protocol Extensions and subclasses

I am wondering why the following doesn't print out what I think it should.
/* Fails */
protocol TheProtocol {
func update()
}
class A: TheProtocol {
}
class B : A {}
extension TheProtocol {
func update() {
print("Called update from TheProtocol")
}
}
extension TheProtocol where Self: B {
func update() {
print("Called update from B")
}
}
let instanceB = B()
instanceB.update()
let instanceBViaProtocol:TheProtocol = B()
instanceBViaProtocol.update()
This will print the following:
Called update from B
Called update from TheProtocol // Why not: Called update from B (extension)
I am especially wondering why
instanceBViaProtocol.update()
Doesn't execute the update() in the extension on TheProtocol:
extension TheProtocol where Self: B {
func update() {
print("Called update from B")
}
}
I would think it would since B inherits from A which adopts TheProtocol, so I would think that B would then implicitly adopt TheProtocol as well.
Moving the protocol adoption to B from A yields the expected result.
protocol TheProtocol {
func update()
}
class A { // Remove TheProtocol
}
class B : A, TheProtocol {} // Add TheProtocol
extension TheProtocol {
func update() {
print("Called update from TheProtocol")
}
}
extension TheProtocol where Self: B {
func update() {
print("Called update from B")
}
}
let instanceB = B()
instanceB.update()
let instanceBViaProtocol:TheProtocol = B()
instanceBViaProtocol.update()
Result:
Called update from B
Called update from B
I took a look at https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94#.6cm4oqaq1 and http://krakendev.io/blog/subclassing-can-suck-and-heres-why, but I was unable to figure this out. Are extension methods not honored on subclasses of entities that adopt the protocol?
The answer is Method Dispatch + a Bug in Swift.
Method dispatch is the mechanism used by the compiler to choose an implementation to execute when a method is invoked. Swift uses 3 kinds of method dispatch. You can read about it here
The dispatch method, is determined by the type of the reference, not by the type of the instance. In your case the reference type is TheProtocol.
let instanceBViaProtocol:TheProtocol = B()
instanceBViaProtocol.update()
You have a protocol extension that defines common behavior for a requirement method. In that case, dynamic dispatch is used. That means that the implementation declared in B should be used. But there is a bug in Swift that causes the issue.
For each type Swift uses a witness table to register the implementations used for dynamic dispatch. The bug causes the B class to fail to register its implementation of update() in the Witness table of TheProtocol. And when update is dispatched through the TheProtocol table, the wrong implementation is used.
Here you have your example with some changes. Notice that if you declare update in the superclass and override it in the subclass, it works as expected. That's the clearest way to see the bug to mee.
protocol TheProtocol {
func update()
}
class A: TheProtocol {
func update(){
print("Implementation of A")
}
}
class B : A {
override func update(){
print("Implementation of B")
}
}
//All those who conform to TheProtocol will execute this.
extension TheProtocol {
func update() {
print("Common: TheProtocol")
}
}
extension TheProtocol where Self: B {
func update() {
print("Common: TheProtocol for B's")
}
}
extension TheProtocol where Self: A {
func update() {
print("Common: TheProtocol for A's")
}
}
let instanceBViaProtocol:TheProtocol = B() //It prints "Implementation of B"
instanceBViaProtocol.update()
I hope this answers you question.
https://www.raizlabs.com/dev/2016/12/swift-method-dispatch/ has an awesome explanation on method dispatch in swift.
Here you can read a short thing I wrote about method dispatch in protocol extensions.

Duplicate codes in Controller

I am currently working on one project. It may potentially have duplicated codes in multiple controllers like below.
Controller A
class A: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
// about 50~70 lines of codes
#IBAction func scanButtonTapped {
// used self (as AVCaptureMetadataOutputObjectsDelegate)
// used view
// called presentViewController(...), which is a func in UIViewController
}
}
Controller B
class B: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBAction func scanButtonTapped {
// will need same logic as in Controller A
}
}
My current solution is have another class C, and move the duplicated codes into it. However, if I do so, controller can cast to AVCaptureMetadataOutputObjectsDelegate, but not to UIViewController.
class C {
func btnTapped (view: UIView, controller: AnyClass) {
// logic is here
// controller can cast to AVCaptureMetadataOutputObjectsDelegate
// but controller cannot cast to UIViewController
}
}
so A and B will have
class A {
#IBAction func scanButtonTapped {
let c = C()
c.btnTapped(view, self)
}
}
My question is if it is possible to cast controller into UIViewController. OR is there another way to refactor the codes properly?
What about extend AVCaptureMetadataOutputObjectsDelegate protocol and create default implementation by protocol extension (POP approach)?
protocol ScanButtonClickable: AVCaptureMetadataOutputObjectsDelegate {
func btnTapped() // this line is optional
}
extension Clickable where Self: UIViewController {
func btnTapped() {
// logic is here
}
}
class A: UIViewController, ButtonClickable {
...
}
class B: UIViewController, ButtonClickable {
...
}
Try this:
//declare your default method to be used across classes
protocol MyProtocol {
func myFunc()
}
//provide the implementation of your default behavior here in myFunc()
extension MyProtocol {
func myFunc() {
print("Default behavior")
}
}
class A: MyProtocol {
}
class B: MyProtocol {
}
let a = A()
a.myFunc()
let b = B()
b.myFunc()
//prints
Default behavior
Default behavior

Set delegate to class type not to instance in Swift

In Objective-C it was possible to set a class as a delegate (not an instance of a class but a pure class). Is it possible in Swift?
Yes it is
Declare a delegate variable to be a class type, not instance type.
I also make it optional, but we could also make it non-optional and pass it in the init method.
var delegate : Int.Type?
Code Example
class A {
static func sayHello() {
println("Hello")
}
}
class B {
var num = 10
var delegate : A.Type?
func hi() {
delegate?.sayHello()
}
}
var b = B()
b.delegate = A.self
b.hi()

Why custom delegate in iOS is not called

I am trying to create a custom delegate using playground in swift. However the doSomething method is not being called through callback.
It seems that delegate?.doSomething() does not fire to the XYZ class doSomething method.
Thanks in advance!
import UIKit
#objc protocol RequestDelegate
{
func doSomething();
optional func requestPrinting(item : String,id : Int)
}
class ABC
{
var delegate : RequestDelegate?
func executerequest() {
delegate?.doSomething()
println("ok delegate method will be calling")
}
}
class XYZ : RequestDelegate
{
init()
{
var a = ABC()
a.delegate = self
}
func doSomething() {
println("this is the protocol method")
}
}
var a = ABC()
a.executerequest()
It seems that delegate?.doSomething() does not fire to the XYZ class
doSomething method.
That is correct. class ABC has an optional delegate property, but the value of
the property is nowhere set. So the delegate is nil
and therefore the optional chaining
delegate?.doSomething()
simply does nothing. Also you have defined a class XYZ but
not created any instances of that class.
If you set the delegate of a to an instance of XYZ then
it will work as expected:
var a = ABC()
a.delegate = XYZ()
a.executerequest()

Resources