Openapi-generator: [C#] Codegen for Unity

Created on 20 Aug 2018  路  13Comments  路  Source: OpenAPITools/openapi-generator

Description

This is an issue to discuss how a C# generator can be effectively useful in a Unity project.
I've been told the C# codegen is under a refactor phase, so my suggestion is to consider the next few points, to maybe include in this refactor.

I don't know if Unity will require a separate template, but it's a possibility.

Related issues/PRs

This issue will extend the points discussed here, kindly consider taking a look at it before continuing:
https://github.com/swagger-api/swagger-codegen/issues/8569

Suggest a fix/enhancement

Here's a list of things one might want to consider when supporting codegen for Unity:

  • Using UnityWebRequest (see issue above): the usual way to call a UnityWebRequest and wait for a response is through Coroutines (example) or through the completed event.

Note that the response is returned on the main thread (and of course, the deserialization of the response should happen on a background thread).

Also, this can easily be made into an async/await style by creating an Awaiter, maybe something like this:

    //UnityWebRequestAsyncOperation is an AsyncOperation
    public static TaskAwaiter GetAwaiter(this AsyncOperation asyncOperation)
    {
        var taskCompletionSource = new TaskCompletionSource<bool>();

        asyncOperation.completed += operation => { taskCompletionSource.TrySetResult(true); };

        return ((Task) taskCompletionSource.Task).GetAwaiter();
    }

Which will bring me to the next point:

  • Depending on the configuration of a Unity project, the .Net version running can be 3.5 or 4.x equivalent. For that reason, some C# features are not always accessible. Fortunately, Unity has define symbols allowing to identify the configuration, and have code to support it accordingly. For example:
public class API {
//
//APIs using Coroutines here, which is supported on both runtime versions
//

#if NET_4_6

//
//APIs using async/await here, only available in 4.6
//

#endif
}
  • As for Json serialization, the obvious choice would be Newtonsoft's, although it is known that there are few modifications needed for it to work correctly on different platforms. This asset could be of help.

  • The IL2CPP backend converts C# code into C++, for performance purposes. While this is a great feature, it has few limitations, including working with generic virtual methods (see the bottom of the page).
    In one of the cases (as an example), the deserialization fails because the converter tries to construct a CollectionWrapper<decimal?> object, which was not generated. So what I had to do is to make a call to it somewhere, so the the IL2CPP backend would pick it up, and generate the C++ equivalent:

#if ENABLE_IL2CPP
//THIS IS NEEDED SO THAT IL2CPP CAN JSON DESERIALIZE List<decimal?>
    void UsedOnlyForAOTCodeGeneration()
    {
        new CollectionWrapper<decimal?>((IList<decimal?>) new List<decimal?>());
    }
#endif
}

Once the constructor is actually called anywhere in the code, the code converter picks it up and generate equivalent C++ code for it.

Note 1: When il2cpp backend is used, the symbol ENABLE_IL2CPP is defined.
Note 2: in this case, CollectionWrapper is internal, that's why I had to make it public, and recompile the Json.Net library. For this reason, any method generic virtual method needed for the correct deserialization can be generated accordingly, as needed (a work around for private/internal types might be needed)

  • Script compilation and assembly definition files
    Unity recompiles the project after every code change automatically. Assembly definition files can divide code into separate assemblies/projects, for faster compilation. An OpenAPI generated project can have it's own probably.

  • In my previous issue, I mention editor extensions to make it easier to generate code. This can be a separate project which makes a call to the C# generator (either through a CLI or a rest service). This is something I have implemented with the current C# generator of swagger, and it helped me iterate faster (after changing a json file containing my spec, I can regenerate the code with a click of a button, see image from previous issue)

  • Unity mostly doesn't need any build scripts, only raw C# files.

  • Supporting Unity Test Runner

Please feel free to ask questions, and discuss. But from my experience with codegen and Unity, these are the main points that would make working with REST APIs a seamless experience when using Unity.

C-Sharp Feature Discussion

All 13 comments

@wing328 Here's my take on a Unity C# generator

@iBicha thanks for sharing your feedback. I've copied the C# technical committee below:

cc @mandrean (2017/08) @jimschubert (2017/09)

What's stopping you from using the unity editor integrated JSON utility? Code generation will never run at runtime. I think that's better than introducing a new dependency (aka newtonsoft)

That's a good question. Just to clarify, the JSON deserialization will run at runtime during api calls. But the reason the json utility probably not the best suited is because it's very limited when it comes to supported types.

@iBicha have you taken a look at the refactor work you've linked? I'm wondering if the API abstractions I've made would support the UnityWebRequest cleanly.

The intention of the refactor is that it allows users to write their own simple adaptors for api query operations such that you can have a task-based asynchronous client or a synchronous client. The API implementation types then allow you to set these client instances directly.

So, for example, you could create a file, UnityAsyncClient:

class UnityAsyncClient : IAsynchronousClient {
    public Task<ApiResponse<T>> GetAsync<T>(String path, RequestOptions options, IReadableConfiguration configuration = null) {
        var config = configuration ?? GlobalConfiguration.Instance;
        using (UnityWebRequest www = UnityWebRequest.Get(config.BasePath + path))
        {
            var query = www.Send();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                byte[] results = www.downloadHandler.data;

                // await + construct ApiResponse<T>
            }
        }
    }

    // etc.
}

Then, you could construct APIs:

var api = new PetApi(){
   AsynchronousClient = new UnityAsyncClient(),
   SynchronousClient = null // or other implementation
}

I haven't worked with Unity, and their docs look pretty light, so the above is just a copy/paste from their example code.

Sorry it's taking me a while to respond. I've been meaning to respond with proper feedback for some time now but I didn't get the time around it. Will do asap

I've been working on Unity friendly OpenAPI parser and assets generator for some time, it resolves all issues with the multi-platform support and leverage many Unity specific tools to provide an easiest possible way to work with restful API

Maybe we can leverage https://github.com/proyecto26/RestClient instead of using RestSharp.

this feels like a duplicate of #6800 in some ways, AFAIK the biggest blocker to using any of the current generators in a unity project is the use of restsharp

@nk2580 have you considered the suggestion by @jimschubert: https://github.com/OpenAPITools/openapi-generator/issues/853#issuecomment-417163199 ?

@wing328 when it鈥檚 not a part of a unity package that works fine. When considering a unity package it鈥檚 a pretty big issue to rely upon restsharp when there鈥檚 no official package for it.

@nk2580 ok. What would you suggest to replace RestSharp?

My previous suggestion: https://github.com/OpenAPITools/openapi-generator/issues/853#issuecomment-477476526

IMO using vanilla net.http is going to be the best and most compatible solution for unity and dotnet

Was this page helpful?
0 / 5 - 0 ratings