Use delegate in objective c class to call swift method - objective-c

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.

Related

Unable to call an Objective C class delegate from a Swift class

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.

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.

Unable to access protocol methods via delegate

While accessing protocol methods via delegate I'm getting following error:
"No known instance method for selector 'lostConnection'"
Swift Protocol:
#objc protocol GameDelegate {
func lostConnection()
}
Objective C game file
//game.h
#protocol GameDelegate;
#interface SSStreamManager : NSObject
#property (assign) id<GameDelegate> delegate
#end
Getting error while calling protocol methods
[self.delegate lostConnection]; // No known instance method for selector 'lostConnection'
You haven't shown any real code, but here's an example that will get you started. These are the three files in an iOS app project:
ViewController.swift
import UIKit
#objc protocol GameDelegate {
func lostConnection()
}
class ViewController: UIViewController {
}
Thing.h
#import <Foundation/Foundation.h>
#protocol GameDelegate;
#interface Thing : NSObject
#property (assign) id<GameDelegate> delegate;
#end
Thing.m
#import "Thing.h"
#import "MyApp-Swift.h"
#implementation Thing
- (void) test {
[self.delegate lostConnection];
}
#end
That compiles. You should be able to follow this model in your own code.

Bridging Swift Initialisers to Objective C

I'm attempting to slowly migrate an Objective C app over to Swift and have started to create new classes -
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
Now in my Objective C .m file I've declared #import MyTarget-Swift.h and in my .h I've added #class MapsAPI which all seems fine however I'm not sure what the Objective C initialisation code should look like. I've tried -
MapsAPI *api = [[MapsAPI alloc] initWithManagerWithDelegate: self];
But that errors with -
No visible #interface for 'MapsAPI' declares the selector
'initWithManagerWithDelegate:'
I've tried looking at the definition of my MyTarget-Swift.h but all that shows is -
SWIFT_CLASS("_TtC4What7MapsAPI")
#interface MapsAPI : NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
#end
Is there something I'm doing wrong here?
You may choose to add #objcMembers to your class declaration:
public class #objcMembers MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
Alternatively (or additionally... who am I to judge) you can mark your initializer as being exposed to Objective-C
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
#objc public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}
And if you want to, you can also explicitly define the Objective-C selector used:
public class MapsAPI : NSObject {
let delegate: MapsAPIResponseDelegate
#objc(initManagerWithDelegate:)
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}

Private methods in objective-c not private

I've created two classes with methods with same name. In one of them it is private, in another - public.
Then somewhere on code i write this:
-(void) doMagic:(id) object {
[(ClassA*)object doSmth];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
ClassB * objB = [[ClassB alloc] init];
[self doMagic:objB];
}
In console i see this:
2012-04-25 23:41:28.183 testmagic[558:403] classB - doSmth
Here's classes' sources:
//.h
#interface ClassA : NSObject
-(void) doSmth;
#end
//.m
#implementation ClassA
-(void)doSmth {
NSLog(#"classA - doSmth");
}
#end
//.h
#interface ClassB : NSObject
#end
//.m
#interface ClassB ()
-(void) doSmth;
#end;
#implementation ClassB
- (void)doSmth {
NSLog(#"classB - doSmth");
}
#end
I know, it's because of "message" nature of methods in Obj-C, and at runtime class possibly do not know which of it's methods are private or public, but here's the question:
How can i make really private method? I heard that with decompiling it's possible to see methods names, so someone can just use my private API. How can i prevent it?
The runtime cannot call what it never knows about. The approach I typically take is to use a static function:
MONObject.h
#interface MONObject : NSObject
// ...
#end
MONObject.m
// 'private' methods and ivars are also visible here
#interface MONObject()
// ...
#end
// typically here:
static void fn(MONObject * const self) {
NSLog(#"%#", [self description]);
}
#implementation MONObject
// ...
// sometimes here:
static void fn2(MONObject * const self) {
NSLog(#"%#", [self description]);
}
#end
A workaround to your problem could be to use a proxy/façade class which internally aggregates an instance of your private class. E.g.:
// .h
#interface FoobarFacade: NSObject
- (void)publicMethod;
#end
// .m
#interface FoobarFacade ()
{
Foobar* impl;
}
#end
#interface Foobar: NSObject
- (void)secretMethod;
#end
#implementation Foobar
- (void)secretMethod { NSLog(#"foobar secret method"); }
#end
#implementation FoobarFacade
- (void)publicMethod {
NSLog(#"façade public method");
[impl secretMethod]; // calling into the secret method
}
#end
Of course this isn't 100% safe either, the runtime puts no barriers as others already told.
Right now you can't have truly private methods. When you are declaring a method in a class extension in the .m file you are just hiding it from being exposed in the public header fle. What you are doing now is considered good design because you are hiding the method from the header file which means people would have to go to some length to find those hidden methods, but they can still find them.
Basically the rule I follow is to put as little as I can into the public header as possible and to put everything else into a class extension. This is all you can really do for now.
If you declare the method in the .h file is public. If you want private visibility you have to declare the method in your .m for example:
#interface ClassB (Private_Methods)
- (void)doSmth;
#end
#implementation ClassB
//Rest of .m