Swift cannot pass class conforming to protocol as function parameter to a function residing in Objective-C file - objective-c

Hi I'm new to Swift but experienced with Objective-C.
I have a project that uses both Swift and Objective-C files (bridging and all).
Say I have a protocol called "fooProtocol" and a class "foo" that implements it. I am trying to pass an object of type "fooProtocol" from the Swift file as a parameter to the function inside the Objective-C file.
here is the Objective-C function inside class "tester":
-(void)setWithFoo:(id<fooProtocol>*)_foo{
}
here is the Swift code:
var myObject:fooProtocol = foo.init()
var objcObject:tester = tester.init()
objcObject.setWithFoo(_foo: myObject)
It first says "Cannot convert value of type "fooProtocol" to expected argument type "AutoreleasingUnsafeMutablePointer (obviously because it needs to be passed by reference, so...)
I then tried casting the parameter to this:
tester.setWithFoo(_foo: AutoreleasingUnsafeMutablePointer<fooProtocol>(myObject))
Now the error reads: "Cannot invoke initializer for type 'AutoreleasingUnsafeMutablePointer with an argument list of type '(fooProtocol)'
I have tried many more permutations and variations but I simply cannot stop the compiler error. For such a simple procedure as passing a polymorphic variable to a function in Objective-C file that expects that protocol id, Swift has made it a nightmare.
...Any help would be appreciated, thanks!
=== EDIT ===
Here are the declarations for the classes, now starting properly with caps
In the "FooProtocol.h" file:
#protocol FooProtocol
#end
In the "Foo.h" file:
#import <Foundation/Foundation.h>
#import "FooProtocol.h"
#interface Foo : NSObject <FooProtocol>
#end
In the "Foo.m":
#import "Foo.h"
#implementation Foo
#end
The "FooProtocol.h" file:
#import <Foundation/Foundation.h>
#protocol FooProtocol
#end
The "Tester.h" file:
#import <Foundation/Foundation.h>
#import "FooProtocol.h"
#interface Tester : NSObject
-(void)setWithFoo:(id<FooProtocol>*)_foo;
#end
The "Tester.m" file:
#import "Tester.h"
#implementation Tester
-(void)setWithFoo:(id<FooProtocol>*)_foo{
//do something with _foo
}
#end
And again the Swift code that can't compile:
var myObject:FooProtocol = Foo.init()
var objcObject:Tester = Tester.init()
objcObject.setWithFoo(AutoreleasingUnsafeMutablePointer<FooProtocol>(myObject))

You probably don't mean to say this:
-(void)setWithFoo:(id<FooProtocol>*)_foo;
It is very unusual to see an id* in Objective-C. In fact, it's so unusual that in all my years of programming Cocoa, I have never seen one.
You probably mean this:
-(void)setWithFoo:(id<FooProtocol>)_foo;
And then you will be able to say, on the Swift side:
objcObject.setWithFoo(myObject)

Related

Swift class using Objective-C class using Swift class

I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end

Calling objective C from Swift error = Class does not have a member called

I'm trying to call an Objective-C class from Swift. So I enabled the bridging header option
Created the following files:
MyTest.h:
#import <Foundation/Foundation.h>
#interface MyTest : NSObject
+(void)sayHello;
#end
MyTest.m
#implementation MyTest
+(void)sayHello {
NSLog(#"Hello");
}
#end
at MyProject-Bridging-Header.hadded:
#import "MyTest.h"
Then when in a Swift file I call:
let test = MyTest()
test.sayHello()
The compiler complains in the second line:
MyTest does not have a member named 'sayHello'
What am I doing wrong?
Note: I have no experience with Objective-C.
You created a class method, not an instance method.
Try instead:
MyTest.sayHello()
Or change the objective-c function to:
-(void)sayHello
Note the - instead of the +. See this question and answers for further reading.

Conflicting parameter types in implementation of 'Login:': '__strong id' vs '__strong Callback' (aka 'void (^__strong)(RESTResponse *__strong)')

very fresh to objective C, cant figure out what i am missing.
.h file
#import <Foundation/Foundation.h>
#import "RESTResponse.h"
typedef void (^Callback)(RESTResponse*);
#interface AquaUser : NSObject
....
-(void)Login:Callback;
-(void)Register:Callback;
#end
.m file
-(void)Login:(Callback) handler
{
...
RESTResponse *result = [RESTResponse new];
result.sucesss = true ;
result.response = #"Login succesfull";
handler(result);
...
}
in .m i get warning on the declaration of Login and register
Conflicting parameter types in implementation of 'Login:': '_strong id' vs '_strong Callback' (aka 'void (^_strong)(RESTResponse *_strong)')
although code compile and work, i am tryinf to learn the lesson here. help appreciated.
Looks like you are missing the types in your .h file.
#interface AquaUser : NSObject
....
-(void)Login:(Callback)handler;
-(void)Register:(Callback)handler;
#end
Edit:
Just a side note, in objective c, it's best to make your methods start with a lower case character. Not a big deal but it's common practice.
Happens also if you don't #import class that is declaring used Type in your .h file and use #class instead. #class doesn't work in this case. I had an enum defined in class I only mentioned with #class and wrote function that uses that enum in .h of my other class. Switching to #import worked.

Cocoa: Build Warning that s Forward Declared Class's and #interface May not Exist

I am trying to build the Clustering Plug in my project under Leopard. I have following two questions.
In the project an interface class is defined as
#interface ClusteringController : NSWindowController
{
.......
.....
....
}
#end.
And this class is used in implementation class using forward declaration:
#class ClusteringController;
then in one function it is used as:
- (long) filterImage:(NSString*) menuName
{
ClusteringController *cluster = [[ClusteringController alloc] init];
[cluster showWindow:self];
return 0;
}
When I build this project it produces the warning:
warning: receiver 'ClusteringController' is a forward class and corresponding #interface may not exist
Also there is one more warning produced:
warning: no '-updateProxyWhenReconnect' method found
This warning is coming for the following line of code:
if(delegate) [delegate updateProxyWhenReconnect];
Can anybody help me to overcome these warnings?
A forward declaration is used when the header file will be imported after the interface. It looks to me that you've used the #class directive after the interface for the class itself.
The normal use of a forward class declaration looks like this:
#import "SomeSuperClass.h"
#class Forwardclass;
#interface SomeClass : SomeSuperClass
{
Forwardclass anIvar;
}
#property Forwardclass anIvar;
#end
#import "SomeClass.h"
#import "ForwardClass.h"
#implementation SomeClass
#synthesize anIvar;
-(void) setAnIvar:(ForwardClass *) aForwardClass;
#end
The #class directive is never used in an implementation (.m) file.
That's not what #class is for.
You use #class in the header file for another class, to tell the compiler that the class you're declaring does exist. Without it, the compiler would not know that that's a class name, and when you declare a variable as holding a pointer to an instance of that class, the compiler would think that you're just making up words. Using #class is called forward-declaring the class.
Your situation is different. You're in the implementation file for that class.
What the compiler needs from you now is the class's #interface. The warning is telling you that the compiler needs an #interface, but you haven't given it one (so, as far it knows, the #interface “may not exist”).
Normally, you would have written the #interface in a header file; how now to get it into the implementation file?
That's where the preprocessor comes in, with its #import directive. At the top of the implementation file (ClusteringController.m), import the header file:
#import "ClusteringController.h"
The preprocessor will replace this with the contents of that file, then hand the preprocessed code to the compiler, which will see the #interface there.

how to return C++ pointer in objective-C++

I have the following objective-C++ header with the simple method to return this pointer.
#interface MyObj
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
I have created the simple method
#implementation MyObj
-(MyCPPObj *) getObj
{
return cpp;
}
Everything seems to work until I actually try to use the object in another file
newObj = [createdMyObj getObj];
It complains: error: cannot convert 'objc_object*' to 'MyCPPObje *' in initialization.
It seems that the method is return an objective-c object, but I specifically requested a C++ pointer.
MyCPPObj is an honest C++ class:
class MyCPPObj
{
public:
int x;
}
How can I fix that?
On my 10.6.3 machine, the following combination worked without any problem: aho.h
#import <Foundation/Foundation.h>
class MyCPPObj{
};
#interface MyObj:NSObject
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
#end
and aho.mm
#import <Foundation/Foundation.h>
#import "aho.h"
void foo(){
MyObj*objcObj=[[MyObj alloc] init];
MyCPPObj*cppObj=[objcObj getObj];
}
Two pitfalls you might have fallen into:
Unlike C++, a class in Objective-C which doesn't inherit from NSObject won't work. (Well, you can make it work, but you don't want that usually.) Note the line #interface MyObj:NSObject.
To use NSObject, do #import <Foundation/Foundation.h>
Don't forget to use the extension .mm for Objective-C++ files.
Most likely you have forgotten to #import the header file with the #interface into the .mm file where you use getObj.
The error states what happens, and JeremyP is right on the money. When you forget to include a header file with the prototypes of the selectors, the compiler assumes the selector returns an object of type id. Well id is a typedef to objc_object*, which is incompatible with your C++ class. To fix the error, you simply need to include your header file in the file where you called getObj.