Asynchronous REST API web calls from HoloLens apps using Unity and Visual Studio

Asynchronous REST API web calls from HoloLens apps using Unity and Visual Studio

When you build HoloLens apps using Unity and Visual Studio you will come across some difficulties when implementing web calls. Especially when web calls takes a long time. That will cause holographic objects like your gaze cursor to freeze. The problem is that Unity is not thread safe. That means that Unity does not really allow you to use any asynchronous calls.

Unity equivalent

Unity provides a method StartCoroutine(IEnumerator routine) on the MonoBehaviour class. This method returns a Coroutine class which is used only to reference the coroutine itself. It allows you to execute behaviour over multiple frames and can be seen as some kind of asynchronous call. It has no overhead for the app whatsoever which makes it handy to use. But keep in mind that running time consuming code within the method specified by the StartCoroutine will freeze your frames causing the hold your scene containing the holographic objects.

Truly asynchronous calls

If you want truly asynchronous calls you will need to run your code in a separate thread. Threading will enable your app to accomplish work asynchronously in parallel threads. We want to use Windows.System.Threading.ThreadPool.RunAsync(WorkItemHandler) to execute asynchronous web calls.

HoloLens applications are just Microsoft Windows Store Apps. An apps support functionality like the ThreadPool class. But the problem is that it is not accepted by Unity. Unity is based on .NET 3.5 framework and does not support multi-threading. That means as soon as you start rebuilding your project from Unity you will have build errors. And that means the Visual Studio project can not be build or rebuild.

Therefor our class will be setup in such a way that by using a compiler directive the asynchronous code is not compiled during the build via Unity. After Unity has created the Visual Studio solution and projects you will need to add the compiler directive. That compiler directive causes the asynchronous code to be included in the build for HoloLens.

Implementing the skeleton of the API class

The skeleton of the class is based on a singleton principle and contains some includes for our asynchronous code. We will be using a (made-up and original) compiler directive PLATFORM_HOLOLENS to exclude/include the code in builds. By adding the compiler directive to the HoloLens project as a conditional compiler symbol the code is included in the build.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using UnityEngine;
#if (PLATFORM_HOLOLENS)
using Windows.Foundation;
#endif
public class API
{
public delegate void OnGetDataCompleted(string id, string json);
private static API instance = null;
private API()
{
}
public static API Instance
{
get
{
if (instance == null)
{
instance = new API();
}
return instance;
}
}
}

Implementing the POST method

We going to implement the first method for an asynchronous POST action. A POST action allows us to send data to a specific URL. In most cases you need to send additional data along with the call. In our example we use a JSON string converted to a byte array. The calling method will use something like jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);

public void PostDataAsync(string url, byte[] jsonBytes)
{
#if (PLATFORM_HOLOLENS)
    IAsyncAction asyncAction = Windows.System.Threading.ThreadPool.RunAsync(
    async (workItem) =>
    {
        WebRequest webRequest = WebRequest.Create(url);
        webRequest.Method = "POST";
        webRequest.Headers["Content-Type"] = "application/json";
        Stream stream = await webRequest.GetRequestStreamAsync();
        stream.Write(jsonBytes, 0, jsonBytes.Length);
        WebResponse response = await webRequest.GetResponseAsync();
    }
    );
    asyncAction.Completed = new AsyncActionCompletedHandler(PostDataAsyncCompleted);
#endif
}
#if (PLATFORM_HOLOLENS)
private void PostDataAsyncCompleted(IAsyncAction asyncInfo, AsyncStatus asyncStatus)
{
}
#endif

The method uses an asynchronous method inside the ThreadPool.RunAsync() method. That method returns an interface to a work item. Inside the method a WebRequest is build and used to write through a Stream object the JSON data into the request. Finally the web call is made through GetResponseAsync(). Keep in mind that the PostDataAsyncCompleted method is called when the GetResponseAsync() method has finished. Meaning that any data set after the GetResponseAsync() method is not seen by that event method.

Implementing the GET method

Next we are going to implement the second method for an asynchronous GET action. A GET action allows us to get data returned by calling a specific URL. Normally you use the URL to provide additional data (e.g. https://contoso.com/api/content/5)  to a REST API call. So in most cases you do need to add additional data. In this example we left it out. But if needed it has the same implementation as the POST method.

  
public delegate void OnGetDataCompleted(string id, string json);
 
    public void GetDataAsync(string url, string id, OnGetDataCompleted handler)
    {
#if (PLATFORM_HOLOLENS)
        IAsyncAction asyncAction = Windows.System.Threading.ThreadPool.RunAsync(
            async (workItem) =>
            {
                try
                {
                    WebRequest webRequest = WebRequest.Create(url);
                    webRequest.Method = "GET";
                    webRequest.Headers["Content-Type"] = "application/json";
                    WebResponse response = await webRequest.GetResponseAsync();
                    Stream result = response.GetResponseStream();
                    StreamReader reader = new StreamReader(result);
                    string json = reader.ReadToEnd();
                    handler(id, json);
                }
                catch (Exception)
                {
                    // handle errors
                }
            }
            );
        asyncAction.Completed = new AsyncActionCompletedHandler(GetDataAsyncCompleted);
#endif
    }
#if (PLATFORM_HOLOLENS)
    private void GetDataAsyncCompleted(IAsyncAction asyncInfo, AsyncStatus asyncStatus)
    {
    }
#endif

Again the method uses an asynchronous method inside the ThreadPool.RunAsync() method. That method returns an interface to a work item. Inside the method a WebRequest is build and used to call the GetResponseAsync() method. A stream object is used to retrieve the result data from the web call. As you can see in the code we have implemented the Completed event with the GetDataAsyncCompleted() method. But be aware! This method is called as soon as the GetResponseAsync() method is finished. So handling the returned data in the completed method is not possible. Therefor we use a delegate. The method based on the delegate is given through the parameters and is used to send a completed message containing the id and data. So the calling party on the GET method can give it’s own method based on the delegate to do additional actions after the response has completed.

Conclusion

It is possible to use asynchronous calls using HoloLens development with Unity and Visual Studio. The problem arises when you use stuff which is part of .NET framework above version 3.5. By using a compiler directive you can ensure that changes made in Unity which causes to do a build from Unity will still work. Keep in mind that rebuilding your Visual Studio solution will reset the conditional compiler symbols. That means that after a rebuild with Unity you need to add PLATFORM_HOLOLENS again at the project Assembly-CSharp project in your solution. The downside is that you can’t test or run your asynchronous code in Unity. It is not part of the build due to the compiler directive and the non-ability of Unity. If you want to do testing from Unity I would  recommend to add an additional non asynchronous method part using for example the WWW class or the UnityWebRequest class to perform the POST and GET method.

I will be posting shortly another blog post which uses this class to update text around a holographic object based on an Azure Cloud Service.

 

 

 

This Post Has 12 Comments

  1. Adam Tuliper

    Good post, thanks. Consider rather than PLATFORM_HOLOLENS try the built in UNITY_UWP (which is geared for this out of the box)

    1. Adam Tuliper

      Specifically while in Unity if(!UNITY_EDITOR && UNITY_UWP) or NETFX_CORE (the latter is only enable when doing your final compile in VS while UNITY_UWP is set once your build target is set, hence the check also for !UNITY_EDITOR to make sure you aren’t running in the editor when compiling which must mean you are building)

      1. Alexander Meijers

        The problem is with UNITY_EDITOR that it does not work. I tried that first before i started using an own compiler directive. UNITY_EDITOR is only to determine if you are in the editor, while i just wanted nothing of the code to be compiled when working in Unity regardless which mode i am in.

  2. Van Binh Nguyen

    Really nice article. Would be perfect, if you add a usage example.

    So how do I use it?
    I assume I create an API Object?
    Something like this?

    API apiObject = new API();
    apiObject.PostDataAsync(“http://stackoverflow”,{});

    Greets Senador

    1. Alexander Meijers

      Hi…

      Use API api = new API.Instance;

      And indeed than call the functions

  3. MartaTw

    Hi,

    I am doing get request in orderto get some data. Using Your code I get the following error:

    SetActive can only be called from the main thread.
    Constructors and field initializers will be executed from the loading thread when loading a scene.
    Don’t use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

    Could You please help me to solve it?

    My BR
    Marta

    1. Alexander Meijers

      I’m not sure what you are doing exactly. When do you call SetActive? Do you have some coding example?

  4. vivek

    thanks for creating such good article, easy to understand and use..
    I know basic C# and Unity, where to learn advanced C# like networking, Threads, Events
    can you refer me any book or tutorials..
    thanks,
    vivek,
    Indie game developer

  5. Bradford Brown

    I do not understand how to implement this code please advise

Leave a Reply