Unable to call an Objective C class delegate from a Swift class - objective-c

My issue here is that I need the "protocolMethod" in the OldViewController.m to be called from the NewViewController.swift in a delegate fashion.
The project I'm working on was completely written in Objective C, and there's a new View Controller that was written in Swift.
The new View Controller is supposed to call a delegate method, conformed by the Objective C caller object, so the Objective C View Controller can process that call.
The app runs and the delegate is called from Swift, but never actually executed in the Objective C View Controller.
I've tried including all the sample files and the code that's supposed to be called, so here it is:
MyProtocol.h
#import <Foundation/Foundation.h>
#protocol MyProtocol <NSObject>
- (void)protocolMethod:(NSString*)input;
#end
My-Bridging-Header.h
#ifndef My_Bridging_Header_h
#define My_Bridging_Header_h
#import <UIKit/UIKit.h>
#import "OldViewController.h"
#import "MyProtocol.h"
#endif
OldViewController.h
#import <UIKit/UIKit.h>
#interface OldViewController : UIViewController
#end
OldViewController.m
#interface OldViewController ()<MyProtocol>
#end
#implementation OldViewController
//THIS IS THE PROBLEM: NEVER GETS CALLED
- (void)protocolMethod:(NSString *)input {
NSLog(#"protocolMethod:%#", input);
}
- (IBAction)myButtonClicked:(UIButton *)sender {
NewViewController *newViewController =[self.storyboard instantiateViewControllerWithIdentifier:#"NewViewController"];
[newViewController setModalTransitionStyle:UIModalTransitionStyleCoverVertical];
[self presentViewController:newViewController animated:YES completion:nil];
}
#end
NewViewController.swift
import UIKit
class NewViewController: UIViewController {
#objc weak var myProtocol: MyProtocol?
override func viewWillAppear(_ animated: Bool) {
self.myProtocol?.protocolMethod("testing 123")
}
}
Thank you for your help!

Call a objective-C protocol method in Objective-C class from Swift
define the protocol in Objective-c MyProtocol.h
#ifndef MyProtocol_h
#define MyProtocol_h
#import <Foundation/Foundation.h>
#protocol MyProtocol <NSObject>
#optional
- (void)protoMethod:(NSString *)input;
#end
#endif
define interface of LoginViewController.h
#import <UIKit/UIKit.h>
#import "MyProtocol.h"
#class NewViewController; //declare knowledge of swift class
#interface LoginViewController : UIViewController <MyProtocol>
// method to trigger testing manual
-(void)haveFun;
#end
define implementation of LoginViewController.m
#import "LoginViewController.h"
#import "YourProductModulName-Swift.h" // will bridge exposed swift class to objc
#implementation LoginViewController
-(void)protoMethod:(NSString*)input {
NSLog(#"protocolMethod:%#", input);
}
- (void)haveFun {
// did not use the storyboard to make testing simple
// this is an #objc exposed swift class
NewViewController *newViewController = [[NewViewController alloc] init];
// now trigger predefined method that checks for protocol
[newViewController fun:self];
}
#end
now let us write a swift class that is subclassing an NSObject, here UIViewController and expose it to objc
import Foundation
import UIKit
#objc //expose class when you need to use it from Objective-C
class NewViewController: UIViewController {
// as objc bridging is working with NSObjects,
// we can use it instead of Any, which can't be exposed.
#objc(foo) weak var foo : NSObject?
override func viewWillAppear(_ animated: Bool) {
// question was: how to call (id<MyProtocol>)-(void)protoMethod:
// as we dont use storyboard for testing here..
// nothing in here
}
// no autonaming, defined -(void)fun: instead
#objc(fun:) func fun(protocolObj: MyProtocol) {
let bar = protocolObj
bar.protoMethod!("direct invokation of protoMethod")
// ah why not, lets remember the object for later.
foo = bar as? NSObject
testme()
}
#objc func testme() {
// as foo is NSObject here, check if exist
if (foo != nil) {
// cast foo and check for protocol
if let loginview = foo as? MyProtocol {
loginview.protoMethod!("PROTOCOL-LOL!")
} else {
print("i will allways love youuu!")
}
}
}
}
now lets have some fun and use the back and forth bridged stuff.
This will allocate a Objective-c class LoginViewController following <MyProtocol> and invoke -(void)haveFun method. This method will allocate a Swift Class NewViewController (important! based on NSObject) that will invoke #objc exposed swift func fun: and give self as parameter that must follow the MyProtocol. It will then invoke the protocol method of LoginViewController with a parameter NSString and store the LoginViewController object in weak var foo as NSObject. Then it will trigger internal swift testme() and use the stored foo to check again if it exists and conforms to protocol MyProtocol and invokes the protocolMethod -(void)protoMethod:(NSString*)string; again or print something if not conforming.
LoginViewController *login = [[LoginViewController alloc] init];
[login haveFun];
Don't forget the example here needs Swift bridging to Objective-C and Objective-C bridging to Swift. If that is a perfect design pattern is another talk.

Related

Swift protocol called in Objective-C not working and application crashes with error message "unrecognized selector"

I am trying to get the Swift protocol to work in Objective-C file, but the application crashes when the error as below.
+[OpenCamera onCameraClose]: unrecognized selector sent to class 0x102ff8580
I am not sure as to what I am missing.
//Swift: UIViewController Code
#objc protocol CameraViewControllerDelegate {
func onCameraClose()
}
#objc class CameraViewController: UIViewController {
var delegate : CameraViewControllerDelegate? = nil
func closeCamera(sender: Any) {
delegate?.onCameraClose()
}
}
// Objective-C : UIViewController Code
OpenCamera.h
#interface OpenCamera : UIViewController <CameraViewControllerDelegate>
OpenCamera.m
#import <MyProjectName/MyProjectName-Swift.h>
#implementation OpenCamera
- (void)viewDidLoad {
[super viewDidLoad];
CameraViewController *cameraViewController = [[CameraViewController alloc] initWithNibName:#"CameraView" bundle:nil];
cameraViewController.delegate = self; //Warning - Incompatible pointer types assigning to 'id<CameraViewControllerDelegate> _Nullable' from 'Class'
}
- (void)onCameraClose {
NSLog(#"Swift Protocol method executed from Objective-C");
}
#end
The warning here did in fact predict the crash:
cameraViewController.delegate = self;
//Warning - Incompatible pointer types assigning to 'id<CameraViewControllerDelegate> _Nullable' from 'Class'
Clearly it thinks self is a class, not an instance. That's very odd.
My guess is that there is something wrong with your import arrangements, but you have not shown enough information to see what it is. I'll just show an arrangement that works.
Let's assume you have both Objective-C and Swift code in one target (i.e. that no frameworks are involved). Then in Swift, you say:
#objc protocol CameraViewControllerDelegate {
func onCameraClose()
}
class CameraViewController: UIViewController {
#objc var delegate : CameraViewControllerDelegate? = nil
func closeCamera(sender: Any) {
delegate?.onCameraClose()
}
}
Note the use of #objc var to expose the delegate property. There is no need to expose the class to Objective-C, as it is already an NSObject derivative.
Okay, in Objective-C, here is your interface file:
#import <UIKit/UIKit.h>
#interface OpenCamera : UIViewController
#end
Note that you do not import the generated header in a .h file, and you do not attempt to mention an imported protocol here.
On to the implementation file:
#import "OpenCamera.h"
#import "MyProject-Swift.h"
#interface OpenCamera () <CameraViewControllerDelegate>
#end
#implementation OpenCamera
- (void)viewDidLoad {
[super viewDidLoad];
CameraViewController *cameraViewController = [[CameraViewController alloc] initWithNibName:#"CameraView" bundle:nil];
cameraViewController.delegate = self;
}
- (void)onCameraClose {
NSLog(#"Swift Protocol method executed from Objective-C");
}
#end
We import the corresponding .h file and the generated header .h file here. We use an anonymous category to declare conformance to the protocol, and the rest is as you have it. You won't see any warnings.

Delegate from Obj-C into Swift - method is never executed

Before I was using NSNotificationEvents, but as I know I should be using delegate because of 1:1 relations (it will be used only in one place in code).
Basically Im new to objective-C and swift (Im JavaScript guy) and the code that I created after a few hours of reading and checking - it just doesnt work, because self.delegate in not defined.
Im trying to delegate "an event" from objective-C code to swift code. NSNotificationEvents works like charm, but I want to make it with protocols / delegate.
I guess, I just missing how to exactly initialize the delegate protocol.
ProtocolDelegate.h
#import <Foundation/Foundation.h>
#protocol ProtocolDelegate <NSObject>
- (void) delegateMethod: (id)data;
#end
Event.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
#import "ProtocolDelegate.h"
#interface Event : NSObject <RCTBridgeModule, ProtocolDelegate>
#property(nonatomic, weak)id <ProtocolDelegate> delegate;
#end
Event.m
(someMethod is executed from JavaScript React-native code, but it shouldn't be any difference from what I know)
#import "Event.h"
#implementation Event
RCT_EXPORT_MODULE(Events);
RCT_EXPORT_METHOD(someMethod: (NSString*)parameter) {
NSDictionary* data = #{#"parameter": parameter};
NSLog(#"This I can see");
if ([self.delegate respondsToSelector:#selector(delegateMethod:)]) {
NSLog(#"This I can not see");
[self.delegate delegateMethod:data];
}
}
ViewController.swift
class ViewController: UIViewController, ProtocolDelegate {
// Xcode has automatically created this method
func delegateMethod(_ data: Any!) {
os_log("ON AUCTION");
}
}
I want delegateMethod inside ViewController to be executed.
Or maybe I get the whole idea wrong? Objective-C is a bit confising to me.

can't access swift method from objective-c interface

I need to call my swift method from an Objective-C interface, however I keep getting Use of undeclared identifier 'MySwiftClass'.
My swift class works fine on other places. I have #import "myApp-Swift.h" in my objc file and #objc before my swift class. I am not really good with Objective-C, so any help would be really appreciated.
#interface myInterface ()
-(void) logger:(NSArray<MyArray*>*) events withCallback:(void (^)(NSError*)) callback;
#end
#implementation myInterface
-(void) logger:(NSArray<MyArray*>*) events withCallback:(void (^)(NSError*)) callback{
..
[MySwiftClass mySwiftMethod]
..
}
#end
My swift class:
import Foundation
#objcMembers
public class MySwiftClass: NSObject {
#objc
class func mySwiftMethod(){
}
}

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

Use delegate in objective c class to call swift method

I have two files
Question.m
Question.h
These two are written by Objective-C
MainView.swift
This is written by Swift
Question Class has the delegate
#interface Question : NSObject{
id delegate;// put MainViewController here
- (void)trythisfunction{
[delegate test] // compiler doesn't find this method.
}
}
and I make class instance and put MainViewController as delegate of Question in MainViewController.swift
class MainViewController: UIViewController {
override func viewDidLoad(){
q = Question()
q.delegate = self // put self in delegate
}
func test(){
NSLog("test is OK")
}
}
However Compiler found error [delegate test]
Question.m:169:19: No known instance method for selector 'test:'
How can I solve this??
You need to make few changes.
Below class declaration doesn't compile because you can't declare variables inside interface.
#interface Question : NSObject{
id delegate;
- (void)trythisfunction {
[delegate test]
}
}
I have fixed above and the class now looks like this,
# Question.h file
#import <Foundation/Foundation.h>
#interface Question : NSObject
#property (nonatomic, strong) id delegate;
#end
Below is the implementation of the class
# Question.m file
#import "Question.h"
#implementation Question
#synthesize delegate;
- (void)trythisfunction{
[delegate test];
}
#end
As we are integrating this swift and so we will need a Bridging Header whose content look like.
#import "Test.h"
Finally in your swift class now you can import this class
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let q = Test()
q.delegate = self
}
func test(){
NSLog("test is OK")
}
}
And above code works like a charm.