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

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?

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.

Objective-C protocol not being recognized in Swift

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).

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{}

Swift function overriding Objective-C method

I have an Objective-C method (declared in the class named "BaseViewModel" of my old framework) that is :
-(void) updateFromManagedObject:(__kindof NSManagedObject *)entity;
I want to use it in mutiple Swift classes. Each Swift class will use a particular subclass of NSManagedObject and inherits from "BaseViewModel". When i try to override this func like this :
override func updateFromManagedObject(entity: Person?) {
<#code#>
}
OR
override func updateFromManagedObject(entity: Animal?) {
<#code#>
}
the compiler returns :
Method does not override any method from its superclass
It only works with :
override func updateFromManagedObject(entity: NSManagedObject?) {
<#code#>
}
How can I use specifics inherited types of NSManagedObject ? (Maybe with a class Generic-Type ? I try but failed too :/ )
The point of overriding is that the subclass method is called instead of the superclass method when the receiver is an instance of the subclass. Therefore, the subclass method's parameters must handle at least all the parameters the superclass method can handle. So the subclass method's parameters' types must be the same or more general than the parameters' types for the superclass method it overrides.
You cannot overload Objective-C functions based on the argument type, as Objective-C doesn't support this kind of overloading. This is why only the NSManagedObject overriding works.
If you want different behaviour based on the entity type, an alternative would be to declare a Swift-only method:
func updateFromManagedObject2(animal: Animal) {
}
func updateFromManagedObject2(person: Person) {
}
You can't override that method because types in objective-c and swift are a bit different. the types have to line up exactly, or overriding won't work.
Both statements So the subclass method's parameters' types must be the same or more general than the parameters' types for the superclass method it overrides. and You cannot overload Objective-C functions based on the argument type, as Objective-C doesn't support this kind of overloading above are correct.
Another example of overriding illustrating this:
- (UIColor*)getDataPointColor:(int)index
{
return UIColor.whiteColor;
}
override func getDataPointColor(_ index: Int32) -> UIColor {
return UIColor.redColor
}
Note that override func getDataPointColor(_ index: Int) -> UIColor { won't work. (Not sure why, 64-bit code and all...)

Define method with closure in an #objc protocol

I have the following protocol definition:
#objc protocol PersonDataStore {
func findPersonWithId(remoteId: String, completionBlock: ((Person) -> Void)!)
// ...
}
The error I get is that the second parameter cannot be represented in Objective-C. I researched the blocks/closures topic but I have a hard time getting my head around it since it is so conceptually different.
Is this just a matter of syntax or is it really not possible to define an Objective-C protocol with a closure in Swift?
I believe your problem is that your class Person is not a subclass of NSObject. Add #objc to the declaration of your Person class or make it a subclass of NSObject and your protocol definition should work.