Get Objective-C Value in C wrapper (Unity Plugin)

Hey all, i am not familiar with Objecticve-C. I’m using it because I want to natively/correctly check if an app is installed on ios. I found the Objective-C snippet to see if an app is installed and understand needing a C-wrapper to basically have unity talk to the Objective-C script (mm file). Just don’t understand how to get the C-wrapper to get the Objective-C value I have within my mm script.

These are pretty good resources, but may be overkill for what I need:

Figured this would be a good place to get convos started with quick bits that the community can assist in. I know there are workarounds you can do in unity, they’re just not always the correct method/can be buggy at times.

Here’s my script:

//  UnityPluginTest-1.mm
//
//  Created by OJ on 7/13/16.
//
   #import <Foundation/Foundation.h>  
     @interface SampleClass:NSObject
     /* method declaration */
     - (BOOL)isFBInstalledX;
     @end
   
     @implementation SampleClass

//Objective-C value I want      
         - (BOOL)isFBInstalledX {
             return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
         }
   
     @end
   
//C-wrapper that talks to Unity
     extern "C"
     {
         bool isFBInstalled(){
                   
           // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
        
      
          //return -(Bool) isFBInstalledX value  //--this doesn't work
          //return ..... // I give up  :(
   
         }
       
 
     }

You’re going to be using the C# interop features to call your native function and marshall return data back from it.

You basically make a little C# stub that references your bool isFBInstalled() call. Here’s some info:

There MAY be a better way to find out if the user has FB, but perhaps this is the reference method of doing so.

[USER=116149]@Kurt-Dekker - Thanks for the reply. Yea, I know the c# stub I need on the unity end should look like this:

  using UnityEngine;
  using System.Runtime.InteropServices;

    class GetMyOjbectiveCUnityPlugin : MonoBehaviour {

       #if UNITY_IPHONE || UNITY_XBOX360

       // On iOS and Xbox 360 plugins are statically linked into
       // the executable, so we have to use __Internal as the
       // library name.
       [DllImport ("__Internal")]
    private static extern bool isFBInstalled();

       #else

       // Other platforms load plugins dynamically, so pass the name
       // of the plugin's dynamic library.
       [DllImport ("PluginName")]

       #endif

       void Awake () {

       bool FBStatus; //Assign value we recieve to this

          // Calls the isFBInstalled function inside the plugin

       FBStatus = isFBInstalled(); //returns the status of FB install in ObjC plugin
  
       }
    }

I know there is some sort of connectivity, because if you test with a fake bool value to return like below, it sends it back:

//C-wrapper that talks to Unity
     extern "C"
     {
         bool isFBInstalled(){
           
           // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved

     return true; //this sends it back to the C# Unity script


         }

     }

Someone on stackoverflow got back to me with this:

//  UnityPluginTest-1.mm
//
//  Created by OJ on 7/13/16.
//
   #import <Foundation/Foundation.h>
     @interface SampleClass:NSObject
     /* method declaration */
     - (BOOL)isFBInstalledX;
     @end

     @implementation SampleClass
//Objective-C value I want
         - (BOOL)isFBInstalledX {
             return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
         }

     @end

//C-wrapper that talks to Unity
     extern "C"
     {
         bool isFBInstalled(){
           
           // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved


        //Stack overflow response
       return [SimpleClass isFBInstalledX]  //Has X-code compiling issues ("No Class Type")..Not sure if correct because it's a BOOL


         }

     }

Unfortunately this gives compiling issues in Xcode.
On the other hand this has no compiling issues when the method is written like this:

  • (BOOL)isFBInstalledX (with a “+”)
//  UnityPluginTest-1.mm
//
//  Created by OJ on 7/13/16.
//
   #import <Foundation/Foundation.h>
     @interface SampleClass:NSObject
     /* method declaration */
     + (BOOL)isFBInstalledX;
     @end
     @implementation SampleClass
//Objective-C value I want
         + (BOOL)isFBInstalledX {
             return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
         }
     @end
//C-wrapper that talks to Unity
     extern "C"
     {
         bool isFBInstalled(){
           
           // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
        //Stack overflow response
       return [SimpleClass isFBInstalledX]  //This has no Xcode compiling issues, but returns nothing
         }
     }

unfortunately that returns nothing. Not sure if this may be close, but the return method is incorrect because we’re asking a BOOL return (returns yes/no), and not a bool (true/false)[/USER]

Try specifying your C# function stub to just return an int, since in C that’s all a boolean is, either zero or nonzero.

@Kurt-Dekker - Figured it out. Thanks again for your earlier inputs.

First - Apparently apple now requires you to place the URL schemes you’re planning to call in your info.plist -

“If your app is linked on or after iOS 9.0, you must declare the URL schemes you want to pass to this method. Do this by using the LSApplicationQueriesSchemes array in your Xcode project’s Info.plist file. For each URL scheme you want your app to use with this method, add it as a string in this array.” - here’s that resource Apple UIApplication-canOpenURL

In this case my strings would be fb, and yelp.

Second, feel free to use my attached scripts:

  • UnityPluginTest-1.mm (put this in your “Assets>plugins>ios” folder)
  • GetMyOjbectiveCUnityPlugin.cs ( this is the bridge. Place this in the same folder)

To call the FB/Yelp status from a script of your own you can do:

int testfb = GetMyOjbectiveCUnityPlugin().WegotFBApp(); //0 is No, 1 yes
int testyelp = GetMyOjbectiveCUnityPlugin().WegotYelpApp(); //0 is No, 1 yes

Cheers,

OJ

Also, anyone wanting to do the plist pre-loaded so you don’t have to do it manually try these xcode techniques from unity -https://community.unity.com/t5/Unity-Cloud-Build/How-can-you-add-items-to-the-Xcode-project-targets-Info-plist/td-p/2235796

using UnityEngine;
using System.Runtime.InteropServices;

class GetMyOjbectiveCUnityPlugin : MonoBehaviour
{

#if UNITY_IOS
       // On iOS and Xbox 360 plugins are statically linked into
       // the executable, so we have to use __Internal as the
       // library name.
       [DllImport ("__Internal")]
       private static extern int isFBInstalled();
       [DllImport ("__Internal")]
       private static extern int isYelpInstalled();
#else
    // Other platforms load plugins dynamically, so pass the name
    // of the plugin's dynamic library.
    [DllImport("PluginName")]
#endif

    public static int WegotFBApp()
    {
        int FBStatus = 0; //Assign value we recieve to this

        // Calls the isFBInstalled function inside the plugin
        FBStatus = isFBInstalled(); //returns the status of FB install in ObjC plugin

        return FBStatus; // 0 is No, 1 is Yes
    }

    public static int WegotYelpApp()
    {
        int YelpStatus = 0; //Assign value we recieve to this

        // Calls the isFBInstalled function inside the plugin
        YelpStatus = isYelpInstalled(); //returns the status of FB install in ObjC plugin

        return YelpStatus;  // 0 is No, 1 is Yes

    }
}
//  UnityPluginTest-1.mm
//  Created by OJ on 7/13/16.
//  In unity, You'd place this file in your "Assets>plugins>ios" folder

//Objective-C Code
#import <Foundation/Foundation.h>

  @interface SampleClass:NSObject
  /* method declaration */
- (int)isYelpInstalledX;
- (int)isFBInstalledX;
  @end

  @implementation SampleClass

- (int)isYelpInstalledX
   {
       int param = 0;
  
       if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"yelp:"]])
       {  
           param = 1; //Installed
       }else{
           param = 0; //Not Installed
       }

       return param;
    }

- (int)isFBInstalledX
   {
       int param = 0;
  
        if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb:"]])
        {
            param = 1; //Installed
        }else{
            param = 0; //Not Installed
        }
        return param;
    }

  @end

//C-wrapper that Unity communicates with
  extern "C"
  {
      int isFBInstalled()
     {
        SampleClass *status = [[SampleClass alloc] init];
        return [status isFBInstalledX];
     }

      int isYelpInstalled()
    {
        SampleClass *status = [[SampleClass alloc] init];
        return [status isYelpInstalledX];
    }
  }
4 Likes

Hi all,
I have gone through the above stuff and found very well.
But, I have some different scenario:
I have the c# code script like:

public class NativeAPI
{
    [DllImport("__Internal")]
    public static extern string[] getAllXRCloudScenes();
    [DllImport("__Internal")]
    public static extern string[] getAllSmartAssetsForXRCloudScene(string sceneId);
    [DllImport("__Internal")]
    public static extern void loadSmartAsset(string binaryId);
}

So, now I want to write the plugins in Objective-c++(.mm). Could anyone let me know how can I call the C# methods into the .mm plugins to get the array strings.

Basically, We almost have the void function like: “public static extern void sendUnityStateUpdate(string state);”
But I have now array of strings and unable to get the array if I tried like this:
In plugin .h file

- (void) getAllXRCloudScenes:(NSMutableArray*)getAllScenes;

In plugin .mm file

void getAllXRCloudScenes(const NSString* getAllScenes) { return [api getAllXRCloudScenes:[NSMutableArray arrayWithObjects:getAllScenes, nil]]; }

Note: I am using the UnityAsALibrary(UAAL) approach.

Calling from native C back into C# is possible but has a lot of issues that have slowly changed over time as new capabilities are added. I have not bothered to keep up with those new issues and I always just call from C# into my C classes.

Thanks @Kurt-Dekker . I also try to calling from C# to Objective-C++(.mm) class. Therefore I have the C# script which having the arrays and that array I want to have in my .mm class somehow.

Not sure how to cross-platform marshal an array of strings. It’s likely possible, but I’m not really sure of the bookkeeping that might be involved.

I think I would probably just pack it into JSON or some other container and send it over as a single string, then unpack it on the native side, or else send it over one at a time with an index.

For my savedata on my KurtMaster2D games, I actually send each byte of “internal native state” over to the native side one byte at a time, and even the biggest block of data (about 256k) is still only a fraction of a second back and forth.

NOTE: I don’t think it’s possible to call ObjectionableC class methods directly due to name mangling, even static methods. I know this is the case for C++ methods (also due to name mangling) so instead you just call plain old C methods and use the extern "C" { } declarator on anything you want to call on the native side.

1 Like

As well described in this link: here
They have like:

[DllImport ("__Internal")]
   
private static extern float FooPluginFunction();

But what happen if I do something like:

[DllImport ("__Internal")]
   
private static extern string[] FooPluginFunction();

They they call it like:

extern "C" {
  float FooPluginFunction();
}

Stuck point now, how can I call the string array now
maybe:

extern "C" {
  NSString FooPluginFunction();
}

I don’t know about any of the ObjectionableC NS-nonsense stuff… each of those things have to be built in a very specific way or they fail. I would recommend sticking to simple scalar types and arrays of bytes.

Yes, exactly, I am using the extern c { //code here }. But as I said above stuck to call the string[ ].

So, what will you suggest because I am new into the Unity. Any sample code or link would be appreciated.

Definitely start by googling the Unity docs on doing native plugins. There’s plenty of fully-functional examples out there.

1 Like