Using an implementation of a Swift protocol within Obj-C - objective-c

I'm trying to implement a mediator sort of pattern with a mixture of Swift and Obj-C. The issue I'm facing is with how to deal with using the Swift protocol implementation classes from Obj-C. Check out the code to see what I mean:
The Swift protocol and implementation of it:
#objc public protocol TheProtocol {
func someMethod()
}
#objc public class SwiftClass: NSObject, TheProtocol {
public func someMethod() {
print("someMethod Swift")
}
}
The ObjC-implementation of the protocol:
#import "SwiftAndObjC-Swift.h"
#interface ObjCClass : NSObject <TheProtocol>
- (void) someMethod;
#end
#implementation ObjCClass
- (void) someMethod
{
NSLog(#"someMethod ObjC");
}
#end
My question is how is it possible to define some type in ObjC which is capable of referencing either a SwiftClass or an ObjCClass. For example this does not compile:
#import "SwiftAndObjC-Swift.h"
...
TheProtocol *p = [[ObjCClass alloc] init];
// Error: "Use of undeclared identifier TheProtocol"
This will compile:
#class TheProtocol
TheProtocol *p = [[ObjCClass alloc] init];
But not p can't be used:
#class TheProtocol
TheProtocol *p = [[ObjCClass alloc] init];
[p someMethod];
// Error: Receiver type "The Protocol" is a forward declaration"
(Adding casts to the assignment and/or method invocation doesn't help)
Any solutions?

In Objective-C, a protocol is not a type. You should declare your protocol-conforming class like so:
id<TheProtocol> p = [[ObjCClass alloc] init];
The reason why your forward declaration compiled is because that's what forward declarations do - they announce to the compiler that a class exists and that the linker will fill it in later in the build process.
(That's why changing to id p =... works too.)
In Swift, we declare classes with something like:
class MyClass : Superclass, Protocol, AnotherProtocol { ... }
In Objective-C we use this:
#class MyClass : SuperClass <Protocol, AnotherProtocol>
// ...
#end
See the difference? In Swift, protocols and the superclass are mixed into the inheritance declaration, whereas Objective-C treats them very differently.
Protocols and Classes are treated slightly different across the two languages, thus used differently.
id in ObjectiveC is analogous to AnyObject? in Swift. An object which conforms to SomeProtocol is obviously AnyObject or id in Objective-C.

you don't use #class to declare a protocol... instead you have to import your Swift compatibility header which will contain your protocol definition.
Also to declare an object that conforms to a protocol in Obj C, use id<TheProtocol> as your type

Solved it, I just changed the type to id
id p = [[ObjCClass alloc] init];
[p someMethod];

Related

Can not find Swift Protocol declaration in Obj-C class

I have created on Class in Swift and that class and its protocol I am using in Obj-C enabled project but I am getting below error while compiling my project.
cannot find protocol declaration for 'SpeechRecognizerDelegate'; did
you mean 'SFSpeechRecognizerDelegate'?
Can anyone guide me on this how can I use Swift class protocol in my Obj-C class.
Here is my Swift code:
protocol SpeechRecognizerDelegate : class {
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
class SpeechRecognizer: NSObject, SFSpeechRecognizerDelegate {
open weak var delegate: SpeechRecognizerDelegate?
}
Protocol use in Obj-C:
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Let me know if required more info.
Thanks in Advance.
in Swift:
#objc public protocol YOURSwiftDelegate {
func viewReceiptPhoto()
func amountPicked(selected: Int)
}
class YourClass: NSObject {
weak var delegat: YOURSwiftDelegate?
}
in Objective-C headerFile.h
#protocol YOURSwiftDelegate;
#interface YOURController : UIViewController < YOURSwiftDelegate >
in Objective-C Implementation.m
SwiftObject * swiftObject = [SwiftObject alloc] init];
swiftObject.delegate = self
Define your Swift protocol like this inside your Swift file.
#objc protocol SpeechRecognizerDelegate: class{
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
Create a Swift Module inside your project setting then use it. You can find here complete blog for the mix language coding.
Then use Protocol inside Objective C class,
We required to add protocol inside Objective C file -
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Then you need to conform to the protocol methods -
- (void)viewDidLoad {
[super viewDidLoad];
SpeechRecognizer * speechRecognizer = [[SpeechRecognizer alloc] init];
speechRecognizer.delegate = self;
}
#pragma mark - Delegate Methods
-(void)speechRecognitionFinished:(NSString *) transcription{
//Do something here
}
-(void)speechRecognitionError:(NSError *) error{
//Do something here
}
I had a similar issue after following (import header + Objc annotation on protocol). I got the warning when using Swift code from Objective C headers. Solved by only importing into the implementation .m files.
Add #objc attribute to your protocol:
#objc protocol SpeechRecognizerDelegate : class {
//...
}
Include Swift Classes in Objective-C Headers Using Forward Declarations
//MySwiftClass.swift
#objc protocol MySwiftProtocol {}
#objcMembers class MySwiftClass {}
// MyObjcClass.h
#class MySwiftClass;
#protocol MySwiftProtocol;
#interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
#end

Ambiguous reference to member 'delegate'

I'm trying to create an instance of a singleton class written in Objective-C. I must conform to it's delegate protocol. The Swift implementation looks something like this:
class SomeClass: ManagerDelegate {
let manager = Manager.sharedInstance()
func someFunction(){
manager.delegate = self
}
}
The Objective-C singleton class looks something like this:
// header
#protocol ManagerDelegate <NSObject>
#required
-(void)delegateMethod;
#end
#interface Manager : NSObject {
}
+ (id)sharedInstance;
#property (nonatomic, assign) id delegate;
#end
// class file
+ (id) sharedManager{
static Manager *theSharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
theSharedManager = [[self alloc] init];
});
return theSharedManager;
}
- (void) someInternalMethod{
[delegate delegateMethod];
}
Upon specifying the delegate (manager.delegate = self), I receive the error
Ambiguous reference to member 'delegate'
Is the Objective-C class improperly formed or am I implementing it incorrectly? Am I missing something else?
Your problem is caused by the definition of your sharedInstance method in Objective-C. By declaring it as
+ (id)sharedInstance;
it gets converted to sharedInstance() -> AnyObject! in Swift. Swift then doesn't know which of many potential delegate properties that it can find you mean. If you change your Objective C declaration and implementation to be:
+ (instancetype)sharedInstance;
your problem will go away.
And as #rmaddy points out, your delegate should be weak not assign. If you use assign then delegate will be left as an invalid pointer in the event that the delegate is deallocated.
Also, the use of a delegate with a singleton is a bit contradictory. Given that you will only ever have one instance of the Manager which may be used by a number of other objects and you can only have one active delegate at a time there is the potential for conflicts.
I would suggest that either blocks/closures or NSNotification is a better approach to call backs from a singleton.
Its because whenever you bring instance from objective c code to swift it gives as AnyObject so
let manager = Manager.sharedInstance() //manager is AnyObject due to bridging between objc and swift
gives you AnyObject for manager property. You need to forcefully cast that AnyObject to Manager to use it as manager's instance like below.
class SomeClass: NSObject, ManagerDelegate {
let manager = Manager.sharedInstance() as! Manager //here was your error
func someFunction(){
manager.delegate = self
}
func delegateMethod() {
print("Hello")
}
}
Hope this solves your problem :)

From another class, how do I call a method that's declared in the implementation file but not interface?

In this tutorial here: http://www.raywenderlich.com/62989/introduction-c-ios-developers-part-1
It mentions that for Objective-C:
Even if you only declare a method inside the implementation of a
class, and don’t expose it in the interface, you technically could
still call that method externally.
How is this done?
There are a lot of ways.
For example, as long as a compatible method is declared somewhere, you can call it normally with dynamic typing. Here's a demonstration:
// MyClass.h
#interface MyClass : NSObject
#end
// MyClass.m
#interface MyClass()
- (void)addObject;
#end
#implementation MyClass
- (void)addObject:(id)object {
NSLog(#"Whoa, I got called!");
}
#end
// main.m
#import <Foundation/Foundation.h>
#import "MyClass.h"
int main() {
id something = [[MyClass alloc] init];
[something addObject:#"Look ma, no errors!"];
return 0;
}
Since there is a known method named addObject: that takes an object, and id variables are dynamically typed, this is 100% valid and will call MyClass's addObject: method.
They could even get it with a statically typed variable and a method that isn't known by declaring the method in a category. A few other options:
using performSelector: as #michaels showed in his answer
going straight to objc_msgSend()
getting the method IMP and calling it directly.
You can use the performSelector: method of NSObject, though the compiler will give you a warning if the selector is not publicly declared anywhere
[someObject performSelector:#selector(someMethod)];

Dependency injection with #protocol?

Can I use a #protocol for interfacing between classes? My main goal is to do some dependency injection like in Java (with interfaces and implements).
I've got the following classes: SignUpServiceImpl (which has an interface called SignUpService) and ServiceHelperImpl (interface is ServiceHelper).
I don't want to hard wire both implementations together so I use a #protocol in ServiceHelper which is implemented by ServiceHelperImpl. Then SignUpServiceImpl is initialized with ServiceHelper like this:
- (id)initWithHelper:(ServiceHelper *)myServiceHelper
Is what I'm trying to accomplish possible? It looks so much easier in Java....
An objc protocol is very similar to a Java interface.
The blocking point for you may be how you expect things are actually tied together -- or protocol syntax.
Declare a protocol:
#protocol ServiceHelperProtocol
- (void)help;
#end
Use it in a class:
#interface SomeClass : NSObject
- (id)initWithServiceHelper:(id<ServiceHelperProtocol>)inServiceHelper;
#end
#implementation SomeClass
- (id)initWithServiceHelper:(id<ServiceHelperProtocol>)inServiceHelper
{
self = [super init];
if (nil != self) {
[inServiceHelper help];
}
return self;
}
#end
MONHelper adopts the protocol:
#interface MONHelper : NSObject < ServiceHelperProtocol >
...
#end
#implementation MONHelper
- (void)help { NSLog(#"helping..."); }
#end
In use:
MONHelper * helper = [MONHelper new];
SomeClass * someClass = [[SomeClass alloc] initWithServiceHelper:helper];
...
To accept an object that conforms to a protocol, your init method should be like this:
- (id)initWithHelper:(id<ServiceHelper>)myServiceHelper
If you want to keep a number of different implementations behind a unified class interface, one way to do this in Objective-C is to create an abstract class SignUpService, then in the init method of SignUpService, instead of returning self you actually return a instance of the class that you want to implement it, so in your case, SignUpServiceImpl.
This is how certain class clusters in Cocoa like NSString work.
Let me know if you need more info.

Dynamically invoke a class method in Objective C

Suppose I have Objective C interface SomeClass which has a class method called someMethod:
#interface SomeClass : NSObject {
}
+ (id)someMethod;
#end
In some other interface I want to have a helper method that would dynamically invoke someMethod on a class like this:
[someOtherObject invokeSelector:#selector(someMethod) forClass:[SomeClass class];
What should be the implementation for invokeSelector? Is it possible at all?
- (void)invokeSelector:(SEL)aSelector forClass:(Class)aClass {
// ???
}
Instead of:
[someOtherObject invokeSelector:#selector(someMethod) forClass:[SomeClass class];
call:
[[SomeClass class] performSelector:#selector(someMethod)];
Example (using GNUstep ...)
file A.h
#import <Foundation/Foundation.h>
#interface A : NSObject {}
- (NSString *)description;
+ (NSString *)action;
#end
file A.m
#import <Foundation/Foundation.h>
#import "A.h"
#implementation A
- (NSString *)description
{
return [NSString stringWithString: #"A"];
}
+ (NSString *)action
{
return [NSString stringWithString:#"A::action"];
}
#end
Somewhere else:
A *a = [[A class] performSelector:#selector(action)];
NSLog(#"%#",a);
Output:
2009-11-22 23:32:41.974 abc[3200] A::action
nice explanation from http://www.cocoabuilder.com/archive/cocoa/197631-how-do-classes-respond-to-performselector.html:
"In Objective-C, a class object gets all the instance methods of the
root class for its hierarchy. This means that every class object
that descends from NSObject gets all of NSObject's instance methods -
including performSelector:."
In Objective-C, classes are objects as well. The class objects are treated differently, however, as they can call the instance methods of their root class (NSObject or NSProxy in Cocoa).
So it's possible to use all the instance methods defined in NSObject on class objects as well and the right way to dynamically invoke a class method is:
[aClass performSelector:#selector(aSelector)];
The apple docs are a bit more specific.
You shouldn't implement this yourself.
The NSObject Protocol has a performSelector: method that does exactly this.
Is this built-in method what you want?
id objc_msgSend(id theReceiver, SEL theSelector, ...)
(See the runtime reference docs for this function.)