Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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

Discussion in 'Scripting' started by OJ3D, Jul 18, 2016.

  1. OJ3D

    OJ3D

    Joined:
    Feb 13, 2014
    Posts:
    33
    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:

    Code (CSharp):
    1.  
    2. //  UnityPluginTest-1.mm
    3. //
    4. //  Created by OJ on 7/13/16.
    5. //
    6.    #import <Foundation/Foundation.h>  
    7.      @interface SampleClass:NSObject
    8.      /* method declaration */
    9.      - (BOOL)isFBInstalledX;
    10.      @end
    11.    
    12.      @implementation SampleClass
    13.  
    14. //Objective-C value I want      
    15.          - (BOOL)isFBInstalledX {
    16.              return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
    17.          }
    18.    
    19.      @end
    20.    
    21. //C-wrapper that talks to Unity
    22.      extern "C"
    23.      {
    24.          bool isFBInstalled(){
    25.                    
    26.            // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
    27.        
    28.      
    29.           //return -(Bool) isFBInstalledX value  //--this doesn't work
    30.           //return ..... // I give up  :(
    31.    
    32.          }
    33.        
    34.  
    35.      }
    36.        
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    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:

    https://docs.unity3d.com/Manual/NativePlugins.html

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

    OJ3D

    Joined:
    Feb 13, 2014
    Posts:
    33
    @Kurt-Dekker - Thanks for the reply. Yea, I know the c# stub I need on the unity end should look like this:
    Code (CSharp):
    1.   using UnityEngine;
    2.   using System.Runtime.InteropServices;
    3.  
    4.     class GetMyOjbectiveCUnityPlugin : MonoBehaviour {
    5.  
    6.        #if UNITY_IPHONE || UNITY_XBOX360
    7.  
    8.        // On iOS and Xbox 360 plugins are statically linked into
    9.        // the executable, so we have to use __Internal as the
    10.        // library name.
    11.        [DllImport ("__Internal")]
    12.     private static extern bool isFBInstalled();
    13.  
    14.        #else
    15.  
    16.        // Other platforms load plugins dynamically, so pass the name
    17.        // of the plugin's dynamic library.
    18.        [DllImport ("PluginName")]
    19.  
    20.        #endif
    21.  
    22.        void Awake () {
    23.  
    24.        bool FBStatus; //Assign value we recieve to this
    25.  
    26.           // Calls the isFBInstalled function inside the plugin
    27.  
    28.        FBStatus = isFBInstalled(); //returns the status of FB install in ObjC plugin
    29.  
    30.        }
    31.     }
    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:
    Code (CSharp):
    1. //C-wrapper that talks to Unity
    2.      extern "C"
    3.      {
    4.          bool isFBInstalled(){
    5.            
    6.            // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
    7.  
    8.      return true; //this sends it back to the C# Unity script
    9.  
    10.  
    11.          }
    12.  
    13.      }
    Someone on stackoverflow got back to me with this:
    Code (CSharp):
    1. //  UnityPluginTest-1.mm
    2. //
    3. //  Created by OJ on 7/13/16.
    4. //
    5.    #import <Foundation/Foundation.h>
    6.      @interface SampleClass:NSObject
    7.      /* method declaration */
    8.      - (BOOL)isFBInstalledX;
    9.      @end
    10.  
    11.      @implementation SampleClass
    12. //Objective-C value I want
    13.          - (BOOL)isFBInstalledX {
    14.              return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
    15.          }
    16.  
    17.      @end
    18.  
    19. //C-wrapper that talks to Unity
    20.      extern "C"
    21.      {
    22.          bool isFBInstalled(){
    23.            
    24.            // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
    25.  
    26.  
    27.         //Stack overflow response
    28.        return [SimpleClass isFBInstalledX]  //Has X-code compiling issues ("No Class Type")..Not sure if correct because it's a BOOL
    29.  
    30.  
    31.          }
    32.  
    33.      }
    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 "+")

    Code (CSharp):
    1. //  UnityPluginTest-1.mm
    2. //
    3. //  Created by OJ on 7/13/16.
    4. //
    5.    #import <Foundation/Foundation.h>
    6.      @interface SampleClass:NSObject
    7.      /* method declaration */
    8.      + (BOOL)isFBInstalledX;
    9.      @end
    10.      @implementation SampleClass
    11. //Objective-C value I want
    12.          + (BOOL)isFBInstalledX {
    13.              return [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]];
    14.          }
    15.      @end
    16. //C-wrapper that talks to Unity
    17.      extern "C"
    18.      {
    19.          bool isFBInstalled(){
    20.            
    21.            // Need to get the Objective C BOOL value from above, my c# script will get this value once retrieved
    22.         //Stack overflow response
    23.        return [SimpleClass isFBInstalledX]  //This has no Xcode compiling issues, but returns nothing
    24.          }
    25.      }
    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)
     
    Last edited: Jul 19, 2016
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Try specifying your C# function stub to just return an int, since in C that's all a boolean is, either zero or nonzero.
     
  5. OJ3D

    OJ3D

    Joined:
    Feb 13, 2014
    Posts:
    33
    @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/Unit...Xcode-project-targets-Info-plist/td-p/2235796

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.InteropServices;
    3.  
    4. class GetMyOjbectiveCUnityPlugin : MonoBehaviour
    5. {
    6.  
    7. #if UNITY_IOS
    8.        // On iOS and Xbox 360 plugins are statically linked into
    9.        // the executable, so we have to use __Internal as the
    10.        // library name.
    11.        [DllImport ("__Internal")]
    12.        private static extern int isFBInstalled();
    13.        [DllImport ("__Internal")]
    14.        private static extern int isYelpInstalled();
    15. #else
    16.     // Other platforms load plugins dynamically, so pass the name
    17.     // of the plugin's dynamic library.
    18.     [DllImport("PluginName")]
    19. #endif
    20.  
    21.     public static int WegotFBApp()
    22.     {
    23.         int FBStatus = 0; //Assign value we recieve to this
    24.  
    25.         // Calls the isFBInstalled function inside the plugin
    26.         FBStatus = isFBInstalled(); //returns the status of FB install in ObjC plugin
    27.  
    28.         return FBStatus; // 0 is No, 1 is Yes
    29.     }
    30.  
    31.     public static int WegotYelpApp()
    32.     {
    33.         int YelpStatus = 0; //Assign value we recieve to this
    34.  
    35.         // Calls the isFBInstalled function inside the plugin
    36.         YelpStatus = isYelpInstalled(); //returns the status of FB install in ObjC plugin
    37.  
    38.         return YelpStatus;  // 0 is No, 1 is Yes
    39.  
    40.     }
    41. }

    Code (CSharp):
    1. //  UnityPluginTest-1.mm
    2. //  Created by OJ on 7/13/16.
    3. //  In unity, You'd place this file in your "Assets>plugins>ios" folder
    4.  
    5. //Objective-C Code
    6. #import <Foundation/Foundation.h>
    7.  
    8.   @interface SampleClass:NSObject
    9.   /* method declaration */
    10. - (int)isYelpInstalledX;
    11. - (int)isFBInstalledX;
    12.   @end
    13.  
    14.   @implementation SampleClass
    15.  
    16. - (int)isYelpInstalledX
    17.    {
    18.        int param = 0;
    19.  
    20.        if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"yelp:"]])
    21.        {  
    22.            param = 1; //Installed
    23.        }else{
    24.            param = 0; //Not Installed
    25.        }
    26.  
    27.        return param;
    28.     }
    29.  
    30. - (int)isFBInstalledX
    31.    {
    32.        int param = 0;
    33.  
    34.         if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb:"]])
    35.         {
    36.             param = 1; //Installed
    37.         }else{
    38.             param = 0; //Not Installed
    39.         }
    40.         return param;
    41.     }
    42.  
    43.   @end
    44.  
    45. //C-wrapper that Unity communicates with
    46.   extern "C"
    47.   {
    48.       int isFBInstalled()
    49.      {
    50.         SampleClass *status = [[SampleClass alloc] init];
    51.         return [status isFBInstalledX];
    52.      }
    53.  
    54.       int isYelpInstalled()
    55.     {
    56.         SampleClass *status = [[SampleClass alloc] init];
    57.         return [status isYelpInstalledX];
    58.     }
    59.   }
     
    Last edited: Jul 20, 2016
  6. puneetmahali

    puneetmahali

    Joined:
    Jan 21, 2021
    Posts:
    16
    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:

    Code (CSharp):
    1. public class NativeAPI
    2. {
    3.     [DllImport("__Internal")]
    4.     public static extern string[] getAllXRCloudScenes();
    5.     [DllImport("__Internal")]
    6.     public static extern string[] getAllSmartAssetsForXRCloudScene(string sceneId);
    7.     [DllImport("__Internal")]
    8.     public static extern void loadSmartAsset(string binaryId);
    9. }
    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
    Code (CSharp):
    1. - (void) getAllXRCloudScenes:(NSMutableArray*)getAllScenes;
    In plugin .mm file
    Code (CSharp):
    1. void getAllXRCloudScenes(const NSString* getAllScenes) { return [api getAllXRCloudScenes:[NSMutableArray arrayWithObjects:getAllScenes, nil]]; }
    Note: I am using the UnityAsALibrary(UAAL) approach.
     
  7. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    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.
     
  8. puneetmahali

    puneetmahali

    Joined:
    Jan 21, 2021
    Posts:
    16
    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.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    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.
     
    puneetmahali likes this.
  10. puneetmahali

    puneetmahali

    Joined:
    Jan 21, 2021
    Posts:
    16
    As well described in this link: here
    They have like:
    Code (CSharp):
    1. [DllImport ("__Internal")]
    2.    
    3. private static extern float FooPluginFunction();
    But what happen if I do something like:

    Code (CSharp):
    1. [DllImport ("__Internal")]
    2.    
    3. private static extern string[] FooPluginFunction();
    They they call it like:

    Code (CSharp):
    1. extern "C" {
    2.   float FooPluginFunction();
    3. }
    Stuck point now, how can I call the string array now
    maybe:

    Code (CSharp):
    1. extern "C" {
    2.   NSString FooPluginFunction();
    3. }
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    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.
     
  12. puneetmahali

    puneetmahali

    Joined:
    Jan 21, 2021
    Posts:
    16
    Yes, exactly, I am using the
    Code (CSharp):
    1. extern c { //code here }
    . But as I said above stuck to call the string[].
     
  13. puneetmahali

    puneetmahali

    Joined:
    Jan 21, 2021
    Posts:
    16
    So, what will you suggest because I am new into the Unity. Any sample code or link would be appreciated.
     
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,514
    Definitely start by googling the Unity docs on doing native plugins. There's plenty of fully-functional examples out there.
     
    puneetmahali likes this.