Objective-C protocol not being recognized in Swift - objective-c

We are in the process of migrating existing classes from Objective-C to Swift. In a view controller class I am implementing a protocol and attempting to add the view controller as a delegate of an object contained within it. When I try to add 'self' as the object's delegate in Swift I get the following compile error:
Cannot call value of non-function type '((ListenerProtocol) -> Void)?
Here is the existing implementation in Obj-C
#interface SomeViewController : UIViewController <ListenerProtocol> ...
And adding the class as the listener (delegate)
[manager addListener:self];
This works without any problems. But the Swift version fails in what seems like the same code. Here is the Swift version of the same call:
#objc class SomeSwiftViewController: UIViewController, ListenerProtocol ...
And the call to 'addListener'
manager?.addListener(self)
I have verified successfully that 'self' is a ListenerProtocol object at runtime by checking:
if self.conformsToProtocol(ListenerProtocol){
// ...
}
In the object containing the delegate property the addListener method is defined in Objective-C as follows:
- (void)addListener:(id<ListenerProtocol>)listener {
// ...
}
The Swift class completely implements all of the methods defined in ListenerProtocol. I can't understand why this doesn't work in Swift. Can anyone make a suggestion? Thanks!

The problem is not with ListenerProtocol, it's with the protocol that manager implements. Judging by the type, addListener appears to be provided as an optional method. Notice the question mark at the end of the function type:
((ListenerProtocol) -> Void)?
This usually happens with (actually, I think that it only happens with) Objective-C optional protocol methods.
You should be able to write manager?.addListener?(self) or manager?.addListener!(self).

Related

How to implement a swift delegate method in objective-C having parameter?

I have a swift protocol having following delegate method
#objc public protocol CuteDelegate: NSObjectProtocol {
#objc func myCuteFunc(name: NSString)
}
I have declared delegate object in swift as well
weak var delegate : CuteDelegate?
In my objective C controller where I am implementing the above delegate method is as follows
-(void)myCuteFunc:(NSString* )name{
}
But while calling the method in swift controller
self.delegate?.myCuteFunc(name: str as NSString)
I get unrecognized selector sent to instance
Any clue what's the issue
You need to account for the first arguments's name:
Either:
Make your Objective-C function -(void)myCuteFuncWithName:(NSString* )name
Or:
Change your protocol to #objc func myCuteFunc(_ name: NSString) and call it with self.delegate?.myCuteFunc(str)
This is just an artifact of the way Objective-C function names work vs. the way Swift names its arguments. Objective-C has no way of naming the first argument (which is usually described by the function name), so if Swift has a label for the first argument, the convention used is to add With plus the argument name (with the argument name capitalized) to the function name. By adding _, you make the first argument unnamed and that translates better to the Objective-C naming convention.

Implement an Objective-C method with light weight generics in Swift

I have to implement this method in a DataSource protocol of an Objective-C library
(nullable id<SomeClass>)someMethod;
I am trying to implement it in my class in Swift, specifically, the AppDelegate, with what I believe keeps equal the signature
extension AppDelegate: LIBDataSource {
#objc func someMethod<T: SomeClass>() -> T? {
return nil // temporary
}
}
The problem is that
As it is, I have a warning and an error. The error says Method cannot be marked #objc because it has generic parameters (the warning below is also shown)
If I remove #objc, the warning says Non-#objc method 'someMethod()' cannot satisfy optional requirement of #objc protocol LIBDataSource
Is there a way to implement a generic Obj-C method of a Obj-C protocol in Swift? Or do I have to do a separate Objective-C class to accomplish this?
The syntax
id<SomeClass>
is nothing to do with lightweight generics, it means "any Objective-C class as long as it conforms the protocol SomeClass". Your method doesn't need to be generic but it does need to return an object that conforms to the SomeClass protocol. It's signature should probably be something like
func someMethod() -> SomeClass?

Implementing Objective-C protocol in Swift class

I have a comprehension question. I want to use the Dropbox Objective-C framework in an iOS Swift app.
I already imported the framework successfully and set the import clause in the bridging header.
I was also able to run the authorization process so I think the framework works.
Then I try to use a component of the framework which is declared as protocol:
class ViewController: UIViewController, DBRestClientDelegate {
}
I sat the delegate property, called the loadMetadata method and implemented the corresponding event function:
let dbRestClient = DBRestClient(DBSession.shared())
dbRestClient.delegate = self
dbRestClient.loadMetadata("/")
...
func restClient(client: DBRestClient!, loadedMetadata metadata: DBMetadata!) {
}
What I'm wondering is that it seems not necessary to implement all methods of that protocol. Is this correct? In Swift implementing only a part of a protocol is enough?
I ask because the compiler displays no errors but the delegation method is never called.
Generally, in Swift you have to implement ALL methods of a protocol. (See this question about optional protocol methods: How to define optional methods in Swift protocol?)
But as you said, the framework is written in Objective-C. Objective-C supports optional methods in protocols.
#protocol ProtocolName
#required
// list of required methods
#optional
// list of optional methods
#end
Source
That's why you don't necessarily need to implement all methods declared in a protocol. Usually, only the most important methods are marked with #required, because when calling an optional method, you should always check, if the delegate implemented it.

Must my swift function's return type inherit from NSObject

I have some swift function in a swift object of type Helper that returns an object of class Parameter. Parameter is defined as:
class Parameter { }
And my function like this:
func getParameter() -> Parameter { }
When accessing the methods from Helper only those that don't return anything show up. However, getParameter is uncallable. My assumption is that its return type is invisible to Obj-C. Must Parameter extend NSObject in order to work?
If you want to have your Swift classes available to Objective-C, they must inherit from an Objective-C bridged class. That's why your ViewControllers are always available, and for free-standing classes, you must make their superclass NSObject. You can also specifically note in code they are bridged by marking them: #objc class Parameter: NSObject{}

What is the correct way of accessing a Swift Delegate from Objective-C?

Environment: Xcode 6.1.1 & Xcode 6.2 Beta
Greetings:
I need to publish a NSString within a Swift doc from a neighboring Objective-C doc within the same project. For example, display "Hello World" generated in Objective-C upon a Swift page. I've made a proof-of-concept demo; based on feedback.
I'm thinking of using an ObjC --> Swift delegate via a protocol pattern as shown below:
Note: the Swift file is the delegate.
Here I'm calling the delegate method in Swift, from Objective-C:
#pragma mark - Action methods
- (IBAction)sendDelegateAction:(UIButton *)sender {
[_delegate radiusString:#"Hello World"];
}
I've instantiated the Objective-C file to link the delegate to the instance (I hope I got it right):
let geo32Controller = MyObjCTableViewController()
geo32Controller.delegate = self
So far, the compiler complained that the Swift protocol couldn't be found.
Here's the protocol (declared in Swift):
#objc protocol DiscoveryContributeProtocol {
// optional
func radiusString(radiusString:String)
}
And here's the delegate reference to that protocol in the Objective-C header file:
#interface MyObjCTableViewController : UIViewController<UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id<DiscoveryContributeProtocol> delegate;
#end
However, the compiler can't find the protocol:
BTW: when I put the bridge reference in the ObjC's header file, I get a compiler error:
Two Questions:
Do I have the correct pattern (did I instantiate the ObjC correctly) ?
How do I make the Objective-C portion see the Swift protocol for the delegate link?
You have the right idea, but have a few bugs that are preventing this from working.
You've declared Geo32Boundaries as conforming to the DiscoveryContributeProtocol, but it doesn't need to and doesn't actually implement it, it only has a property that conforms to that protocol. That's the source of the "Method 'radiusString:' not implemented" error:
#interface Geo32Boundaries: UIViewController // <-- that's all you need
You're setting the delegate incorrectly -- the code you have there looks like it's trying to set a class instance of Geo32Boundaries to self, but you're also trying to call it like a function. You'll need to set the delegate on a the instance of the Geo32Boundaries view controller that is being presented to the user. I don't know where that code lives, so I can't give a great example, but it'll be something like:
geo32Controller.delegate = self
Lastly, though not a bug, your protocol should really be called DiscoveryContributeDelegate -- we usually don't use "protocol" in the protocol name.