I'm using NSURLSession to connect to a database. I have this already implemented in C++ for Windows and am trying to get it working on iOS also. I have a .h file derived from a base C++ class that is the header for my .mm file. If I'm correct I have to implement all the functions in my .h file in C++. However NSURLSession is an Objective-C function. How do I call an Objective-C method from my C++ function?
I have a C++ function called Connect() where I make a C++ object m_Delegate that has an alloc and init.
this->m_Delegate = [[PrivateNSURLSessionDelegate alloc] initWihParent:this];
//where PrivateNSURLSessionDelegate is the name of my interface.
That interface has -(bool)NSConnect (with implementation in the #implementation) which I'm trying to call from:
void Connect()
{
[PrivateNSURLSessionDelegate NSConnect];
//This however gives me the error: +[PrivateNSRLSessionDelegate NSConnect]: unrecongnized selector sent to class
}
I also tried it using my C++ object
void Connect()
{
[m_Delegate NSConnect];
//This gives me a error that is unrecognized selector sent to instance
}
Is there a better way to do this? I basically want to ask the Objective-C to do all the NSURL stuff and send just the data back to the C++.
I'm completely new to Objective-C so any and all help would be greatly appreciated! Thanks!
-(bool)NSConnect
Here the - indicates it is an instance method. Conversely + would indicate a class method.
That being said, [PrivateNSURLSessionDelegate NSConnect]; calls a class method, since you call it on the interface PrivateNSURLSessionDelegate.
However, this is not defined as it is defined as NSConnect is defined as an instance method (btw the convention is that (instance) methods always start with a lowercase).
[m_Delegate NSConnect];
Does however call the instance method. You should define -(bool)NSConnect in the header file of PrivateNSURLSessionDelegate, not above the #implementation in the implementation file, that makes in a private method and thus inaccessible.
There is Objective-C, which is a superset of C, and Objective-C++, which is a superset of C++. Objective-C++ source code files have a .mm suffix, where Objective-C would have a .m suffix.
You cannot call Objective-C from C++. You can however call Objective-C from Objective-C++, and you can write your usual C++ classes in Objective-C++ as well.
Related
I am trying to make use of an Objective-C API in Swift. I can only seem to call the shareMyInstance() method from Swift, and not the initOpenApi() method for some reason. I'm not sure if there is some sort of scope identifier present in the interface, but I can't make use of initOpenApi, even though both are in the header. I also cannot see the method bodies, but I don't believe that affects the scope of the function.
This is locking me into using Objective-C, because for some reason I can access all of the functions from Objective-C, but only 3 of the 4 from Swift.
Header file (LCOpenSDK_Api.h):
#ifndef LCOpenSDK_LCOpenSDK_Api_h
#define LCOpenSDK_LCOpenSDK_Api_h
#import <Foundation/Foundation.h>
#interface LCOpenSDK_Api: NSObject
+ (LCOpenSDK_Api*) shareMyInstance;
- (id) initOpenApi:(NSString*)addr port:(NSInteger)port CA_PATH:(NSString*)caPath;
- (NSInteger)request:(void*)req resp:(void*)resp timeout:(NSInteger)timeout;
- (void)uninitOpenApi;
#end
#endif
My code (.swift):
import Foundation
#objc(LeChangePlayerView)
class LeChangePlayerView: UIView {
//...
#objc
override init(frame: CGRect) {
super.init(frame: frame)
var lc = LCOpenSDK_Api.shareMyInstance()!; //Fine
//Need this function!
lc.initOpenApi("openapi.easy4ip.com", 443, "") //Value of type 'LCOpenSDK_Api' has no member 'initOpenApi'
}
The only possible other explanation is that there is a different header file with the same name, but different interface, but this is highly unlikely because shareMyInstance, request and unitOpenApi are all available, and going to the definition from within the swift file using Xcode points to the same file. It is a dynamic framework, and as of right now, I can only view the headers, not the method implementations. I'm not sure if there's a solution to this, but this is another problem I could use help with. Could they have locked the original source code somehow, as well as made that specific method private?
Although initOpenApi is an instance method, Swift recognises it as an initialiser as it starts with the word init. Initialisers are translated from Objective-C into Swift-style initialisers.
In Objective-C you would say something like [[LCOpenSDK_Api alloc] initOpenAPI:#"openapi.easy4ip.com", port: 443, CA_PATH: #""]
In Swift the word init is stripped and there is no need to explicitly allocate a new instance:
let lc = LC_OpenSDK_Api(openApi:"openapi.easy4ip.com:, port: 443, CA_PATH:"")
However, you need to refer to the documentation from the framework to determine if you want to access the singleton instance LC_OpenSDK_Api.shareMyInstance or whether you want to create a specific instance as I showed above.
Working on a mixed framework. imported inside the Obj-C file but the internal classes are not visible, only the public ones.
The documentation clearly states the internal clasees should be available between Swift and Obj-C:
Importing Swift into Objective-C To import a set of Swift files in the same framework target as your Objective-C code, you don’t
need to import anything into the umbrella header for the framework.
Instead, import the Xcode-generated header file for your Swift code
into any Objective-C .m file you want to use your Swift code from.
Because the generated header for a framework target is part of the
framework’s public interface, only declarations marked with the public
modifier appear in the generated header for a framework target. You
can still use Swift methods and properties that are marked with the
internal modifier from within the Objective-C part of your framework,
as long they are declared within a class that inherits from an
Objective-C class. For more information on access-level modifiers, see
Access Control in The Swift Programming Language (Swift 2).
Code Sample (Create a new project with a framework)
// SwiftObject.swift
public class SwiftObject: NSObject {
public class func doSomething() {}
}
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
// SomeObject.m file
#implementation SomeObject
- (void)someMethod {
[SwiftObject doSomething];
}
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Use of undeclared identifier
}
#end
As indicated in the docs, declarations marked with internal modifier don't appear in the generated header, so the compiler does not know about them and thus complaints. Of course, you could send messages using performSelector approach, but that's not convenient and bug-prone. We just need to help the compiler know that those declarations are there.
First, we need to use #objc attribute variant that allows you to specify name for your symbol in Objective-C:
// SwiftObject.swift
#objc(SWIFTYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
internal class func doSomething() {}
}
And then you just need to create #interface declaration with the methods you want to use in your code - so the compiler will be happy, and also apply SWIFT_CLASS macro with the symbol name you've specified earlier - so the linker would pick the actual implementation:
// SomeObject.m file
SWIFT_CLASS("SWIFTYetAnotherSwiftObject")
#interface YetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
#implementation SomeObject
- (void)someOtherMethod {
[YetAnotherSwiftObject doSomething]; // Should work now !!!
}
#end
I've used the interface declaration in .m file just for clarity, the better option would be to combine such declarations in .h file, and include it.
By declaring methods in that interface we're making a promise to compiler, and it won't complain if you'll put there a method that does not exist (or with wrong signature, etc.) Obviously, you'll crash in runtime in that case - so be cautious.
For me it just worked by checking: "Allow app extension API only". You find it by going to the project setting, select your target and then it is in the General tab under Deployment Info.
Can someone explain to me, why this does solve the problem?
While the above solution works (https://stackoverflow.com/a/33159964/5945317), it seems overly complicated and unintuitive:
Complicated, because it seems to add more things than necessary – I will provide a smoother solution below.
Unintuitive, because the objc macro SWIFT_CLASS resolves to SWIFT_RUNTIME_NAME, and the provided value is not actually the runtime name – nor is the objc class name in the header matching the Swift attribute param in #objc. Still, surprisingly, the solution works – but to me it is not clear why.
Here is what we have tested in our own project, and believe to be the better solution (using the example above):
// YetAnotherSwiftObject.swift
#objc(OBJCPREFIXYetAnotherSwiftObject)
internal class YetAnotherSwiftObject: NSObject {
#objc internal class func doSomething() {}
}
// OBJCPREFIXYetAnotherSwiftObject.h
#interface OBJCPREFIXYetAnotherSwiftObject : NSObject
+ (void)doSomething;
#end
That's it. The interface looks like a regular objc interface. This gives the added benefit that you can include it in other header files (which you cannot do if you use the SWIFT_CLASS macro, as it comes from the autogenerated Swift header file, which in turn you cannot include in an objc header, due to circular dependency).
On the Swift side, the only thing relevant is that you provide the class with the proper objc name. Mind that I only used the name prefix for language consistency – you can even just use YetAnotherSwiftObject everywhere (i.e., in the objc header and in the #objc attribute in Swift – but you need to keep this attribute with explicit naming in any case, and need to keep it consistent with the class name in the header).
This also makes your life easier if you're in the process of converting your objc framework step by step to Swift. You just keep the objc header as before, and now provide the implementation in Swift.
Methods and properties that are marked with the internal modifier and declared within a class that inherits from an Objective-C class are accessible to the Objective-C runtime.
so let's make use of that:
class MyInternalClass: NSObject {
#objc var internalProperty = 42
}
#interface MyPublicClass()
#end
#implementation MyPublicClass
+ (void) printValue {
Class myInternalClass = NSClassFromString(#"MyPackageNameIfAny.MyInternalClass");
id myInternalClassInstance = [myInternalClass new];
int value = [myInternalClassInstance performSelector:#selector(internalProperty)];
NSLog(#"Value is %d ", value); // "value is 42"
}
#end
Using the SWIFT_CLASS macro and #objc class attribute could easily lead to errors when archiving. This approach is safer.
I downloaded SpringBoardServices.h file given in SpringBoardServices and added it to my project. But how to access one of the method present inside SpringBoardServices.h file. I am trying to call BOOL SBSProcessIDForDisplayIdentifier(CFStringRef identifier, pid_t *pid); this method present inside SpringBoardServices.h from MyClass.m file. How to call above method from my .m file?
I used below approach, but it is returning null.
Class myclass = NSClassFromString(#"SpringBoardServices");
NSLog(#" myclass %#", myclass); //null
id myobj = [[myclass alloc] init];
I downloaded SpringBoardServices.h file from this link.
There are couple of methods to access C methods from private framework:
Method 1:
Link a private framework (similar to the way how you link public framework)
Include .h file
Do a call:
SBSProcessIDForDisplayIdentifier(...)
Method 2:
Load framework in runtime using dlopen
Find a method using dlsym
Do a call
BTW. This is applicable to C method's and second method won't work for ObjectiveC methods.
This question already has answers here:
Pass instance method as function pointer to C Library
(1 answer)
How do I give C function a pointer to 'self' (calling obj) in objective-c?
(2 answers)
Closed 9 years ago.
I have Objective-C class in my iOS project that implements Objective-C and C code in the same class. I changed the extension to .mm and this part goes well. Now I want to set a C method that will call the Objective-C method in the same class. The problem I get is when I am trying to call self from the C method.
Here is the code :
void SetNotificationListeners(){
[self fireSpeechRecognition];
}
the error is :
Use of undeclared identifier 'self'
how can I manage this?
You have to pass the instance pointer to the C function:
void SetNotificationListeners(void *uData)
{
MyClass *obj = (__bridge MyClass *)(uData);
[obj fireSpeechRecognition];
}
- (void)myMethod
{
// Call C function from Objective-C method:
myFunction((__bridge void *)(self));
}
(The "brigde" stuff is needed only if you compile with ARC.)
Either give self as an argument to the function call:
void SetNotificationListeners(void *myObj){
[(MyClass*)myObj fireSpeechRecognition];
}
//in objC
SetNotificationListeners(self);
or have a global variable that holds reference
//global variable
static MyClass *myObj;
//in obj c
myObj = self;
SetNotificationListeners(); //use myObj instead of self in function
The first is better in my opinion.
You don’t have to change the file extension. Objective-C is a superset of C, which means you can use plain C in your Objective-C files as you please.
When you write an implementation of an Objective-C method, that method always executes in context of some particular instance, that’s the self part. You get the self in an Objective-C method automagically, but behind the scenes it’s just passed as an argument to the method, see obc_msgsend.
Your plain C function is not a part of the class (plain C functions never are), therefore there’s no instance associated with it when you call it, there’s no implicit self. If you want the function to call some instance, you have to pass the pointer to that instance explicitly. For example some of the plain C APIs have a “context” pointer you can pass when registering for a callback. See the neighboring answers for examples of this.
In Objective-C methods there are two parameters that are implicitly passed into each method, which are self and _cmd. This is why you can access self from within a method.
You could just pass self as an argument to your c function when calling from an Objective-C method, but with that trivial example I'm not sure why you wouldn't just use a method.
You do not need to change the file extension to .mm unless you are using Objective-C++
I'm trying to assign a function to the AURenderCallback inputProc
int setupRemoteIO(audio unit etc){
inProc.inputProc = playerCallback
}
but it says that playerCallback is not declared in this scope although playerCallback is present in the same file and class as setupRemoteIO.
The player callback is like this
static OSStatus playerCallback(void *inRefCon etc)
What could be the problem?
In C, you need to declare a function before its first use, i.e. higher up in the file than the point where you try to use the function. That's why include files are usually clustered at the top of a file; all of the symbols declared in the headers will be available throughout the code in the including file.
In this case, that means the declaration of your callback:
static OSStatus playerCallback(void *inRefCon etc);
must appear before your setupRemoteIO() function so that the compiler knows the function exists when you come to use it.
As you're on iOS, I'll also make the point that in recent compilers this restriction doesn't apply to Objective-C methods. It used to: you could only use method selectors that had already been seen. But in newer versions of Clang an Objective-C method can make use of a selector declared later in the same file without error.