I have 2 classes,
class A(Obj-C) and
class B(Swift).
Class A has a delegate protocol
#protocol ADelegate<NSObject>
- (void)someMethod:(NSString *)str;
#end
to which class B conforms:
class B: ADelegate { ... }
But the issue is, the other Objective-C classes which use class B as a property can access to those delegate methods to which my Swift class conforms. For example, there is a class C(Obj-C):
#interface Class_C_ViewController ()
#property (nonatomic, strong) B *classB_property;
- (void)viewDidLoad {
[self.classB_property someMethod: #""];
}
#end
So basically that Swift property can access and call the delegate methods from another Obj-C class.
Is there a way to make those methods private, so no other class can call those methods but Swift class?
I really hope that I could explain my issue. If you have any questions please let me know.
Related
I want to call a delegate method from a class method.
My demo code
ClassA.h
#interface ClassA: NSObject<MyProtocol>
+ (void)demoMethod;
#property (nonatomic, weak) id<MyProtocol> delegate;
ClassA.m
+ (void)demoMethod {
if ([self.delegate respondsToSelector:#selector(refreshViewController)]) {
[self.delegate refreshViewController];
}
}
The error in above code is:
No member named 'delegate' in 'struct objc_class'
Member reference type 'struct objc_class *' is a pointer; did you
mean to use '->'?
Please help me out to call a delegate method from a class method
Thanks!!!
You cannot access instance properties from a static (class) function.
In your case, in your demo method, self does not refer to an instance of ClassA, but to ClassA itself.
There is no general solution to this behaviour; they all are kind-of work-arounds, depending on your needs:
Keep a static reference to the instance (somehow a singleton approach)
Don't make demoMethod a static function
Keep the current / latest delegate in a static property and use this
If you want access it from class then delegate should be class variable:
#interface ClassA: NSObject<MyProtocol>
+ (void)demoMethod;
#property (nonatomic, weak, class) id<MyProtocol> delegate;
I m trying to passing values from second class to first class for that I am using protocol and delegate process. Whenever I run my program I am facing below Issue.
No Type or Protocol Named 'locateMeDelegate'
Viewcontroller A .h
#interface first : UIViewController < locateMeDelegate > { }
In my case the issue was caused by importing the delegate's header file to the delegator's class .h file. This seems to create a sort of vicious circle. As soon as I deleted the import statement of the delegate's header from the delegator's .h file, the error went away.
Tipically, if you intend your protocol to be used by other classes you must declare it in the header file like this:
// MyClass.h
#protocol MyProtocol;
#interface MyClass : NSObject
#end
#protocol MyProtocol
- (void) doSomething: (MyClass*) m;
#end
After you declare it, you should implement the methods of the protocol in the implementation file, which should conform to the protocol like this:
// MyClass.m
#implementation MyClass <MyProtocol>
pragma mark - MyProtocol methods
- (void) doSomething: (MyClass *)m {
// method body
}
#end
After these two steps you're ready to use you protocol in any class you desire. For example, let's say we want to pass data to MyClass from other class (e.g. OtherClass.h). You should declare in OtherClass.h a property so that we can refer to MyClass and execute the protocol. Something like this:
// OtherClass.h
#import MyClass.h
#interface OtherClass : NSObject
#property (weak) id<MyProtocol> delegate;
#end
You don't forget to import the header file where you declared your protocol, otherwise Xcode will prompt No Type or protocol named "MyProtocol"
id<MyProtocol> delegate; means you can set as the delegate of OtherClass any object (id) that conforms to the MyProtocol protocol (<MyProtocol>)
Now you can create an OtherClass object from MyClass and set its delegate property to self. Like this:
// MyClass.m
- (void)viewDidLoad() {
OtherClass *otherClass = [[OtherClass alloc] init];
otherClass.delegate = self;
}
It's possible to set the delegate to self because the delegate can be any object and MyClass conforms to MyProtocol.
I hope this can help. If you want to know more about protocols you can refer to this two websites:
Working with Protocols - Apple Documentation
Ry's Objective-C Tutorial (This one is easy to pick up)
I also faced the same issue and it seems the error is from Xcode itself. Please Try running on Physical device. This would solve the issue faced.
I'm writing some Swift classes that build upon functionality in our objective-c app. I have a objective-c class with a delegate that conforms to a protocol. I'm trying to call a method on that delegate from inside of a Swift class I'm simplified it down to this.
FredTestProtocol.h:
#protocol FredTestProtocol
- (void) dumbMethod;
#end
FredTestClass.h:
#import <Foundation/Foundation.h>
#import "FredTestProtocol.h"
#interface FredTestClass : NSObject <FredTestProtocol>
#property (nonatomic, weak) NSObject <FredTestProtocol> *delegate;
#end
FredTestClass.m:
#import "FredTestClass.h"
#implementation FredTestClass
- (void) dumbMethod
{
NSLog(#"Boy, this is a dumb method");
}
#end
FredSwiftClass.swift
import Foundation
class FredSwiftClass {
func test()
{
let ocObject = FredTestClass()
ocObject.delegate.dumbMethod() // Error occurs here.
}
}
The indicated line produces the error "'NSObject' does not have a method named 'dumbMethod'" I've tried a lot of ways to eliminate the error, to no avail. I'm sure I'm missing something really fundamental. Can someone tell me how I should go about calling the delegate method from Swift?
When Swift examines the property delegate it simply sees that is is an NSObject and the fact that you have noted that it implements a protocol is ignored. I can't find any specific documentation as to why this is the case.
You can address this in a couple of ways.
First, you can redefine your delegate property to use class anonymity, then Swift will just see it as some object that implements the protocol -
FredTestClass.h
#import <Foundation/Foundation.h>
#import "FredTestProtocol.h"
#interface FredTestClass : NSObject <FredTestProtocol>
#property id<FredTestProtocol> delegate;
#end
Then your Swift code will compile as written.
or you can leave your delegate definition as is and tell Swift that you want to access the delegate as an instance of an object that implements the protocol via downcast -
FredTestSwift.swift
import Foundation
class FredSwiftClass {
func test()
{
let ocObject = FredTestClass()
let theDelegate=ocObject.delegate as! FredTestProtocol
theDelegate.dumbMethod()
}
}
Pretty sure I've got it.
func test()
{
let ocObject = FredTestClass()
if let myDelegate = ocObject.delegate as? FredTestProtocol
{
myDelegate.dumbMethod()
}
}
I have a superclass A that has a BaseModalViewControllerDelegate protocol and a retain property for the id<BaseModalViewControllerDelegate> delegate.
I also have class B, subclass of A, that has a ModalLoginDelegate protocol and a retain property for the id<ModalLoginDelegate> delegate
Now, I set as setter method for B class delegate this method:
-(void)setDelegate: (id<ModalLoginDelegate>)delegate
{
_delegate = delegate;
[super setDelegate: (id<BaseModalViewControllerDelegate>)delegate;
}
So, there is the RootViewController that implements both protocol, but it inits only B class and it set itself as delegate only for B class because it doesn't know that B class is a subclass of A.
Do you think that this is a correct way to set RootVC as delegate for both protocol? Thanks
ADDING
Setting the protocol of B class as inherited from A class:
#protocol ModalLoginDelegate <BaseModalDelegate>
// delegate method of subclass
#end
Now, my RootVC has not to set itself as delegate of BaseModalViewController. But now, when in my B class I want to call the delegate method of the superclass I'm doing this
if (self.loginDelegate)
{
[self.loginDelegate baseModalViewController: self willDismiss: YES];
}
I think that this is not a very clean way, so I created a public method in the superclass -(void)pressedCloseButton; that it will do this
-(void)pressedCloseButton
{
if (self.delegate)
{
[self.delegate baseModalViewController: self willDismiss: YES];
}
}
And in the subclass:
-(IBAction)closeBtnPressed: (id)sender
{
[super pressedCloseButton];
}
Do you think is right?
A better design is to implement a separate delegate property for your subclass, say loginDelegate. It isn't very good OO design to change the type of a property in a subclass. Most OO languages won't even allow it.
This also ensures that the consuming class is "aware" that there are two separate delegate protocols involved.
RootVC will need to set itself as both delegates if it needs to implement both protocols. You can't expect the class not to know which delegate protocols it needs to implement. If RootVC thinks it is only dealing with the base class then it won't set loginDelegate and won't implement the methods in that protocol.
I don't disagree with Paulw11's answer here at all, but it interesting to note that Apple themselves do this.
example. UIScrollView has a delegate property
#property (weak, nonatomic) id <UIScrollViewDelegate> delegate;
and a subclass, UITableView, has a delegate property
#property (weak, nonatomic) id <UITableViewDelegate> delegate;
When we declare a protocol in ObjC we usually have that protocol extend the < NSObject > protocol.
#protocol BaseModalViewControllerDelegate <NSObject>
-(void)doSomething;
-(NSString *)titleForThing;
#end
Now this protocol above has not only the methods prototyped here but also those in the < NSObject > protocol. Its is very much like this protocol is a 'subclass' of the other protocol, inherits all its stuff too.
If you did this with your second protocol
#protocol ModalLoginDelegate <BaseModalViewControllerDelegate>
-(void)doAnotherThing;
-(NSString *)titleForTheOtherThing;
#end
then what you've done here would be absolutely in line with what Apple have done with UITableView and UIScrollView, because a pointer of type id< ModalLoginDelegate > is always also an object of type id< BaseModalViewControllerDelegate > , just as a UIButton* will always be able to be passed in as a UIView* ...
But without doing this there is a fundamental problem in your
-(void)setDelegate:(id<ModalLoginDelegate>)delegate
method there, because you are assuming this object complies with the BaseModalViewControllerDelegate protocol when the only thing you know for sure is that it complies with the ModalLoginDelegate protocol. Some inherited method may call on self.delegate with a BaseModalViewControllerDelegate method which self.delegate does not respond to..
I hope this helps :)
I have an object which implements various protocols (like 10 different ones).
For example
#interface MyClass <UITableViewDelegate,UITableViewDataSource,UISearchDisplayDelegate,...>
#end
#implementation
/// a whole bunch of methods for the delegates
#end
In order to "clean" things up in this class - I have created helper classes which encapsulates the logic pertaining to those delegates.
so now the new refactored class looks something like
// public interface which looks the same
#interface MyClass <UITableViewDelegate,UITableViewDataSource,UISearchDisplayDelegate,...>
#end
// private interface
#interface MyClass ()
// a bunch of objects which implement those methods
#property (nonatomic,strong) MyUITableviewDelegate *tableViewDelegate;
#property (nonatomic,strong) MyUITableviewDataSource *tableViewDelegate;
#property (nonatomic,strong) MySearchDisplayDelegate *searchDisplayDelegate;
// another bunch of objects which answer to the delegates
#end
#implementation
// all the delegate methods were moved out of here to the class which implements the method
#end
Now when an object of "MyClass" is assigned as a delegate - it should return the runtime object which "answers" to those delegates (for instance if "MyClass" object is assigned as a UITableViewDelegate - the MyUITableviewDelegate should be assigned.
How can this be done?
Must I override the forwardInvocation in the "MyClass" object?
You need to implement forwardingTargetForSelector:
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.tableViewDelegate respondsToSelector:aSelector]) {
return self.tableViewDelegate;
}
// etc
return [super forwardingTargetForSelector:aSelector];
}