Reputation: 154
I'm trying to conform to an Objective-C delegate method on an Objective-C class via a swift extension on said class. The problem is I am given no autocomplete options when defining the body for this function in the swift extension below. I had to manually translate it from Objective-C and it still doesn't work.. I know the delegate is set up correctly as when I provide the delegate function body in the ObjectiveCConformingClass directly it works fine (and autocompletes when I write the function).
I have a swift class like this (what I need to fix I imagine):
extension ObjectiveCConformingClass {
func delegateFunction(with index: Int, viewName: String, action: String, dictionary: [String : Any]) {
//Never gets called.
}
}
Extending a class like this:
Class that conforms to the delegate:
@interface ObjectiveCConformingClass : SuperClass <ObjectiveCDelegate>
//Whatever
@end
Delegate:
@protocol ObjectiveCDelegate <NSObject>
@optional
- (void)delegateFunction:(NSInteger)index
view:(nonnull NSString *)view
action:(nonnull NSString *)action
dictionary:(nonnull NSDictionary<NSString *, id> *)dictionary;
@end
So to summarise: I need to conform to this delegate in a swift extension of the class, not the actual class. It works fine if I do it directly in the objective-C class. Any ideas why it is not working? Or if this is even possible?
Here are some reasonably similar questions that ask for different things so did not help me. (i.e. threads that this is not a duplicate of)
Upvotes: 1
Views: 1061
Reputation: 3368
Take your time when reading..
Think of delegates as id<ProtocolName>
"pointer to another object" that conforms to a protocol.
@protocol ObjectiveCDelegate <NSObject>
@optional
- (void)delegateMethod:(NSInteger)index
view:(nonnull NSString *)view
action:(nonnull NSString *)action
dictionary:(nonnull NSDictionary<NSString *, id> *)dictionary;
@end
Usually your @interface ClassName : NSObject
that wants to make use of the delegate should have a property to keep it around or to set it nil
(nullable) which implies why you want it weak.
@property (nonatomic, weak) id<ObjectiveCDelegate> delegate;
and the class (object) that will become your delegate must conform to this protocol, so you have to declare this in its interface as you did. In full beauty looks like..
@interface ObjectiveCConformingClass : SuperClass <ObjectiveCDelegate>
@property (nonatomic, weak) id<ObjectiveCDelegate> delegate;
-(void)invokeDelegate; //for testing.
@end
Because the protocol above has optional method declaration it will not throw warnings when you did not implement it. To avoid running in trouble when working with the delegate from within your class with ClassName
(object) you will want to check if the delegate property is not nil
and can respond to the desired method name.
@implementation ObjectiveCConformingClass
-(void)delegateMethod:(NSInteger)index view:(NSString *)view action:(NSString *)action dictionary:(NSDictionary<NSString *,id> *)dictionary {
NSLog(@"original objc delegateMethod, called from %@", view);
}
-(void)invokeDelegate {
if (_delegate) {
// you can double check if the delegate is really an id<ProtocolName>
if ([_delegate conformsToProtocol:@protocol(ObjectiveCDelegate)]) {
// well you don't know if delegateMethod was implemented, it was optional
// so you have to check,
// indeed it's implemented above, but it is safe this way.
if ([_delegate respondsToSelector:@selector(delegateMethod:view:action:dictionary:)]) {
//now you can call it safely
[_delegate delegateMethod:0 view:@"original ObjectiveCConformingClass" action:@"a" dictionary:@{@"key":@"value"}];
}
}
} else {
NSLog(@"original ObjectiveCConformingClass delegate is nil");
}
}
@end
so far its working in objective-c
now in swift you can make use of the delegate even in an extension
extension ObjectiveCConformingClass {
func extensionMethod() {
if ((delegate) != nil) {
if ((delegate?.responds(to: #selector(delegateMethod(_:view:action:dictionary:))))!) {
delegate?.delegateMethod?(1,view: "extension ObjectiveCConformingClass",action: "world",dictionary: ["foo":"bar"])
}
} else {
print("extension ObjectiveCConformingClass: delegate is nil")
}
}
// following will make you extreme trouble..
// see the missing _ so this is a different method then the objc variant
// its selector is #selector(delegateMethod(index:view:action:dictionary:)
func delegateMethod(index: Int, view: String, action: String, dictionary: [String : Any]) {
print("swift extension func delegateMethod, called from",view)
}
// #selector() is heavily confused what method to select when uncommented
// because selector is #selector(delegateMethod(_:view:action:dictionary:)
// which is declared in objc and can not be directly extended in swift
//func delegateMethod(_ index: Int, view: String, action: String, dictionary: [String : Any]) {
// print("swift extension func delegateMethod, called from",view)
//}
}
let's check if swift extensions works properly when subclassed.
class ClassOtherName : ObjectiveCConformingClass {
func subclassMethod() {
if (delegate != nil) {
// you still don't know if the method was implemented, so check
if ((delegate?.responds(to: #selector(delegateMethod(_:view:action:dictionary:))))!) {
delegate?.delegateMethod?(1, view:"subclass ClassOtherName", action: "action", dictionary: ["key" : "value"])
} else {
print("delegate seems not conforming to protocol")
}
} else {
print("subclass ClassOtherName delegate is nil")
}
}
// of course you can override in subclasses, even if this was a super protocol method
// see the difference.. _ as argument used here
// because Overriding non-@objc declarations from extensions is not supported
override func delegateMethod(_ index: Int, view: String, action: String, dictionary: [String : Any]) {
print("override func delegateMethod, called from",view)
}
}
lets test
let a = ObjectiveCConformingClass() //extended version
a.extensionMethod() // extension ObjectiveCConformingClass: delegate is nil
//a.subclassMethod() // does not exist in ObjectiveCConformingClass
a.invokeDelegate() // original ObjectiveCConformingClass delegate is nil
let o = ClassOtherName() // subclassed version of extension
o.delegate = a
o.extensionMethod() // original objc delegateMethod, called from extension ObjectiveCConformingClass
o.subclassMethod() // original objc delegateMethod, called from subclass ClassOtherName
o.invokeDelegate() // original objc delegateMethod, called from original ObjectiveCConformingClass
o.delegate = nil
o.extensionMethod() // extension ObjectiveCConformingClass: delegate is nil
o.subclassMethod() // subclass ClassOtherName delegate is nil
o.invokeDelegate() // original ObjectiveCConformingClass delegate is nil
o.delegate = o //aka o == self
o.extensionMethod() // override func delegateMethod, called from extension ObjectiveCConformingClass
o.subclassMethod() // override func delegateMethod, called from subclass ClassOtherName
o.invokeDelegate() // override func delegateMethod, called from original ObjectiveCConformingClass
Hope this is not too confusing but you see who is calling who and what is invoked.
Upvotes: 0