Pass #protocol type in Swift - objective-c

I have an Objective-C method which takes Protocol* type as parameter.
How can I invoke this method in Swift.
Example:
// In Objective-C
#protocol AProtocol <NSObject>
#end
#interface MyClass : NSObject
+ (id)proxyWithProtocol:(Protocol*)protocol;
#end
// I can call this method with a protocol as parameter
[MyClass proxyWithProtocol:#protocol(AProtocol)];
If I want to use MyClass in Swift by bridging. How can I pass a protocol defined in Objective-C to proxyWithProtocol method. Can I even pass a protocol defined in Swift to this method?

You would pass the Objective-C protocol in like so:
MyClass.proxyWithProtocol(AProtocol)
If you wanted to pass in a Swift protocol, you would have to expose that protocol to Objective-C:
#objc protocol MyProtocol {
func someGreatFunc()
}
// ...
MyClass.proxyWithProtocol(MyProtocol)
In Swift 3, depending on the way the class is bridged to Swift, your function might look like this:
MyClass.proxy(with: AProtocol)
MyClass.proxy(with: MyProtocol)
Although the compiler isn't happy with the location of "with" and may complain.

Related

How to create NSMutableArray in which objects follow protocol?

How to create NSMutableArray in which objects follow protocol?
For example in swift I can do something like:
var array:[MyProtocol] = []
How to implement that in objC
In Objective-C you declare a variable of protocol type by declaring it as id<SomeProtocol>, e.g.:
#protocol SomeProtocol<NSObject>
...
#end
#interface SomeClass : NSObject<SomeProtocol> // base class NSObject, implements SomeProtocol
...
#end
#implementation SomeClass
...
#end
// a variable declaration somewhere which holds a reference to any object
// which implements SomeProtocol
id<SomeProtocol> anyObjectImplementingSomeProtocol = SomeClass.new;
Using Objective-C's lightweight generics you can extend this to containers of protocol type, e.g.:
NSMutableArray<id<SomeProtocol>> someArray = NSMutableArray.new;
Warning: Lightweight generics are named that for a reason, it is quite easy to bypass the restrictions imposed by them, e.g. by adding an object which does not implement SomeProtocol to someArray. You don't get the same strong generics as in Swift.
HTH

iOS11 Swift 4 - how to check if Swift class conforms to protocol defined in Objective-C?

I have a legacy code base with code written in Objective-C. I'm adding a new class written in Swift which has to conform to existing protocols defined in Objective-C.
How can I make sure my Swift class correctly implements methods defined in Objective-C protocol?
//In Obj-C
#protocol OBJCLocationObserver <NSObject>
- (void)didUpdateLocationWithModel:(nullable Model *)locationModel
lastLocation:(CLLocationCoordinate2D)lastLocation;
#end
//In Swift
extension SwiftLocationManager : OBJCLocationObserver
{
public func didUpdateLocation(with model: Model?, lastLocation: CLLocationCoordinate2D) {
// How to verify this function signature is actually conforming to the Obj-C protocol and is not a new method?
}
}
[MyClass conformsToProtocol:#protocol(MyProtocol)];
According to Apple Docs you can use conformsToProtocol:which returns a Boolean value that indicates whether the receiver conforms to a given protocol.
Example
#protocol MyProtocol
- (void)helloWorld;
#end
#interface MyClass : NSObject <MyProtocol>
#end
Will be exposed as:
console.log(MyClass.conformsToProtocol(MyProtocol));
var instance = MyClass.alloc().init();
console.log(instance.conformsToProtocol(MyProtocol))
Make sure you #import your protocol definition file into the <ProjectName>-Bridging-Header.h file:
#import "OBJCLocationObserver.h"
And then you should see error messages if your signature does not match.
You can also use Xcode Auto Completion. Type:
public func didUpdateLocation
and Auto Complete suggests:
public func didUpdateLocation(withModel Model?, lastLocation: CLLocationCoordinate2D)
which is different than what you have and explains why it isn't working.
Here is another way to get the interface:
As #MartinR suggested on a comment to another question:
Go to the header file where the protocol is defined, and choose
"Generated Interface" from the "Related Items" popup in the top-left
corner. That will show you the exact Swift method signature that you
have to implement.

How to use NSSet<Class> in Swift (exported as Set<NSObject>)?

I have to fulfill a protocol requirement that is defined in Objective-C like this:
#protocol AProtocol <NSObject>
+ (NSSet<Class> * _Nullable)someClasses;
#end
I want to implement this protocol in a subclass written in Swift. I want to return a Set of classes of another Object. The class I want to return is defined like this:
class B: NSObject {}
The class that conforms to the protocol is defined like this:
class A: NSObject, AProtocol {
static func someClasses() -> Set<NSObject>? {
return [B.self]
}
}
Why is NSSet<Class> bridged to Set<NSObject> instead of Set?
This solution is crashing, how can I solve the problem?
NSSet<Class> is bridged to Set<NSObject> because AnyClass does not conform to Hashable which is a precondition for the ValueType of Set.
It can be solved with the following extension for NSObjectProtocol:
extension NSObjectProtocol where Self: NSObject {
static var objcClass: NSObject {
return (self as AnyObject) as! NSObject
}
}
This returns the class of the object casted as NSObject. It is necessary to cast it first to AnyObject because the type system of Swift is so strong that it would not compile or give a warning when directly casting a type to an instance type. In Objective-C this is fine because Class is also just an object. Since NSObject is implemented in Objective-C and the extension is just for NSObjectProtocol, this is save to use (even with the force cast).
Implementing the extension on NSObjectProtocol and not on NSObject itself brings the positive effect that it is not exported to Objective-C.

Objective-c protocol in swift issue

I have a protocol in objective-c :
#protocol stuffDelegate <NSObject>
- (void) applyFiltersWithCuisines:(NSMutableArray *)cuisinesArray neighborhoods:(NSMutableArray *)neighborhoodsArray vibes:(NSMutableArray *) vibesArray;
#end
and i'm trying to use this method in swift :
func applyFiltersWithCuisines(cuisinesArray : NSMutableArray, neighborhoodsArray : NSMutableArray, vibesArray : NSMutableArray) -> () {
println("do stuff")
}
having issues in swift file :
Type 'controller' does not conform the protocol 'stuffDelegate', any ideas ?
So problem is that it looks different in swift
Your parameter names are different in the protocol and the method. The protocol defines
-applyFiltersWithCuisines:neighborhoods:vibes:
The ObjectiveC signature of your Swift method (the #selector) would be
-applyFiltersWithCuisines:neighborhoodsArray:vibesArray:
They don't match.

Why do Objective-c protocols adopt other protocols?

I've seen Objective-c protocols defined in the following way:
#protocol MyProtocol <SomeOtherProtocol>
// ...
#end
Why do protocols adopt other protocols? I'm especially curious why a protocol would adopt the NSObject protocol.
It is simply the same concept as inheritance for classes.
If a protocol adopt another protocol, it "inherits" the declared methods of this adopted protocol.
The NSObject protocol especially declares methods such as respondsToSelector:. So this is especially useful if you declare a #protocol that have #optional methods, because when you will then call methods on objects conforming this protocol, you will need to check if the object responds to the method before calling it if this method is optional.
#protocol SomeProtocol <NSObject>
-(void)requiredMethod;
#optional
-(void)optionalMethod;
#end
#interface SomeObject : NSObject
-(void)testMyDelegate;
#property(nonatomic, assign) id<SomeProtocol> myDelegate;
#end
#implementation SomeObject
#synthesize myDelegate
-(void)testMyDelegate {
// Here you can call requiredMethod without any checking because it is a required (non-optional) method
[self.myDelegate requiredMethod];
// But as "optionalMethod" is #optional, you have to check if myDelegate implements this method before calling it!
if ([myDelegate respondsToSelector:#selector(optionalMethod)]) {
// And only call it if it is implemented by the receiver
[myDelegate optionalMethod];
}
}
#end
You will only be able to call respondsToSelector on myDelegate if myDelegate is declared as a type that implements respondsToSelector (otherwise you will have some warnings). That's why the <SomeProtocol> protocol needs to adopt itself the <NSObject> protocol, which itself declares this method.
You may think of id<SomeProtocol> as "any object, whatever its type (id), it just has to implement the methods declared in SomeProtocol, including the methods declared in the parent protocol NSObject. So it can be an object of any type but because SomeProtocol adopts the NSObject protocol itself, it is guaranteed that you are allowed to call respondsToSelector on this object, allowing you to check if the object implements a given method before calling it if it is optional.
Note that you may also not make SomeProtocol adopt the NSObject protocol and instead declare your variable as id<SomeProtocol,NSObject> myDelegate so that you can still call respondsToSelector:. But if you do that you will need to declare all your variables this way everywhere you use this protocol... So this is much more logical to make SomeProtocol directly adopt the NSObject protocol ;)
Inheritance...................