Unity native GPS plugin (iOS and Android)

Marcin Kulwicki
9 min readMar 10, 2022

For some reason LocationInfo.latitude from Unity return latitude as a float, but devices, when you’re using Android Studio or Xcode returns double. When you want better precision or more details like, for example satellites count you need to access to native functions written in java or objective-c.

The answer is to make a plugin with native code. There is a plugin ready, through which i learned step by step how to do it.

This plugin has more features than only GPS. I recommend you check it out.

There you will find a complete project from this story. It can come in handy when you need downloading entire classes.

Or you can test it on the Unity Asset Store
https://assetstore.unity.com/packages/slug/216027

For this tutorial, you only need basic Unity skills.

Versions:

Unity 2021.1.12f1

Android Studio Bumblebee | 2021.1.1 Patch 1

Xcode Version 13.2.1 (13C100)

Agenda:

  1. Prepare Unity project,
  2. Make iOS plugin,
  3. Implement iOS plugin in Unity,
  4. Test iOS plugin,
  5. Make Android plugin,
  6. Implement Android plugin in Unity,
  7. Test Android plugin.

Prepare Unity project

This project will have a separate project for Unity and for Android. Therefore:

  1. Create folder and name it unity-native-gps-plugin,
  2. In this folder create Unity project and name it ‘Unity’,
  3. Open Unity project and rename main scene to NativeGPS,
  4. Create two folders in Assets. Plugins/Android and Plugin/iOS,
  5. You can change game resolution to something more like a phone display,
  6. On the scene, create Canvas with the image object stretched across the entire view. This will be the background. And a text object where all the information will be displayed,
  7. Create Scripts folder in Assets and two scripts. NativeGPSPlugin.cs and NativeGPSUI.cs. NativeGPSUI.cs add as a component to Canvas.
Point 5
Point 6 and 7

Make iOS Plugin

The following part focus on native iOS plugin.

  1. Fist of all, change platform to iOS,
  2. In PlayerSettings add descriptions for usages,
  3. In this story i’ll show you how to test an iOS app on a simulator. Therefor, change the Target SDK to Simulation SDK. But you can check how it works on real device and leave Device SDK,
  4. In Assets/Plugins/iOS create LocaleTools.mm and LocaleTools.h.
Pont 1
Point 2
Point 3
Point 4

First take *.h file and define functions. Init and UpdateLocation methods.

Next *.mm. As you can see there is only one property, latitude. In next part we will use more of them. Longitude, speed, altitude etc. You can download complete files from project shared above.

There are two section. On top, native methods and in extern “C”, something like bridge between your c# unity code and objective-c xcode code. This methods will be called by Unity.

After build project you can find *.mm file in Xcode project hierarchy and edit this file with IntelliSense.

Implement iOS plugin in Unity

This section focus on calling native methods via c# scripts, thus go to NativeGPSPlugin.cs.

In region Dll imports, word extern isn’t random. Extern refers to this extern functions in *.mm file. All this functions should be private and have his own public extraction. For example if you want getLatitude from another script, in your plugin you should a have private reference to the extern method and a public method that will check all conditions and if condition is true, it will return latitdue.

Region init singleton.

Ok, then next. Try to get latitude and show value on the screen. Open NativeGPSUI.cs and change some lines to look like this.

We need reference to Text field. Run the method that calls the StartLocation method, and when the location service is ready, update text field with latitude value.

Don’t forget to attach text filed to NativeGPSUI (Script) in canvas on scene.

Test iOS plugin

This is always the most stressful moment, but also the most exciting.

  1. Add NativeGPS scene to build scene list,
  2. Build project,
  3. Run on device,
  4. Allow access to location.
Point 1
Point 4

And we have the result. The text box is not in the best place, you might want to think about moving it elsewhere. On the middle maybe.

In simulation you can change location to check if latitude will update. You can make it in Features->Location->Custom Location…

Make Android plugin

When creating iOS plugin, the *.mm file is enough, but for Android it will be necessary to generate *.aar file. For this, you need to create a new Android project in unity-native-gps-plugin folder.

  1. Create a new project with or without activity,
  2. Set the package name and project location. I recommend to naming project Android. The minimum SKD is important. If you set it to API 27, your plugin will only work with device with API 27 or higher,
  3. Change view from Android to Project,
  4. Add new module (our plugin),
  5. Create the module as an Android Library. The package name is important as you will be use them in Unity,
  6. Change your view to Android and check if your hierarchy looks the same.
Point 2
Point 3
Point 4
Point 5
Point 6

Our plugin needs to extend UnityPlayerActivity as you guessed it isn’t a base class for the Android enviroment. But that is true for Unity, but not all versions of Unity. I had classes.jar files that didin’t have the UnityPlayerActivity class. But most of all, UnityPlayerActivity is in the classes.jar, witch is included in Unity installation hierarchy. Unity -> Hub -> Editor -> 2019.4.18f1 -> PlaybackEngines -> AndroidPlayer -> Variations -> il2cpp -> Release -> Classes -> classes.jar . It is safer to use classes.jar from https://github.com/MarcinKulwicki/unity-native-gps-plugin/tree/main/Android/LocaleTools/libs .

  1. Copy classes.jar into LocaleTools/libs. The libs folder is only displayed in project view,
  2. Load library,
  3. Add to LocaleTools,
  4. Check if classes.jar is unpacked. Compare with the photo below.
Point 1
Point 2
Point 3
Point 4

Now we need to create two classes for our module. Main.java and Location.java.

Main.java
Location.java

Main.java is only used to call the gps init method and get the properties. In Location.java are two functions, like in iOS. Init location service and update vales on change. In the init method, some line showing an error.

You need to add permission for location in LocaleTools/manifest/AndroidManifest.xml. Click Add permission or manually add in *.xml file.

There will still be a bug but we have to leave it as we will check the permissions with a Unity script.

Ok. Now we need add two things in GrandleScript/build.gradle from the Module: Android.LocaleTools. The first is task to compile the plugin and second is to change dependencies. Suppose you build a plugin witout changing dependencies, the plugin has been successfully created, but you get a build gradle error when building your Unity project. The error will say duplicated files from classes.jar. So change the dependency and add taks, copyPlugin.

Now run the plugin build job. Click the little play button next to the task.

If all goes well, you will see BUILD SUCCESSFUL and *.arr file should be in the Unity project in Assets/Plugins/Android.

Implement Android plugin in Unity

At the beginning, switch build platform to Android. And open NativeGPSPlugin.cs.

Add a static reference to AndroidJavaClass, this object is used for init and calling each native method.

  1. In the singleton init method, you should construct the AndroidJavaClass class with a parameter pointing to the Main class from the LocaleTools plugin,
  2. In the StartLocation method, add a connection to android plugin startLocation method,
  3. Make a handy Get method for all getters,
  4. Call get method in GetLatitude.
Point 1
Point 2
Point 3
Point 4

Now let’s open NativeGPSUI.cs and:

  1. Add using for Android,
  2. Add two properties,
  3. Update start method. Now it should be able to check if the location permission is true and if so, call StartLocation. There you will find a recommendation from Unity https://docs.unity3d.com/Manual/android-RequestingPermissions.html ,
  4. Create PermissionsRationaleDialog.cs,
  5. In NativeGPSUI create onGUI method for PermissionsRationaleDialog.cs,
  6. Finally you need to change API level like in plugin and you can build.
Point 1
Point 2
Point 3
Point 4
Point 5
Point 6

Test Android plugin

Build on device and check the result.

That’s all. Now you should be able to write your own native Unity plugin. There are many steps, but if you want to use some method that is implemented natively on the device but not in Unity. This way you can use them. Hopefully everything is clear, but if not, you can still download the finished project and reverse engineer yourself.

I want to thank the developers of the unity-native-toolkit I use for understanding how it does this.

--

--