I am using the Unity PlayFab SDK and wanting to convert the provided async callback style API to use async/await to make the code more readable. I am trying to write a generic method to wrap any arbitrary API call into a Task. I am very close to getting this working, but can't figure out how to set the error property on the error callback.
Here's my example:
public static class PFAsyncWorks
{
public delegate void PlayFabMethod<TIn, TOut>( TIn r, Action<TOut> o , Action<PlayFabError> e, object c, Dictionary<string, string> h);
public static Task<TOut> Run<TIn, TOut>( PlayFabMethod<TIn,TOut> playfabMethod,
TIn request, object customData = null, Dictionary<string, string> extraHeaders = null )
{
var tcs = new TaskCompletionSource<TOut>();
Action<TOut> resultCallback = result => tcs.SetResult( result );
void errorCallback(PlayFabError error)
{
var ret = default(TOut);
//THIS LINE GIVES ERROR - WORKS IF I COMMENT IT OUT BUT THEN ERRORS ARE SWALLOWED
((PlayFabResult<TOut>)ret).Error = error;
tcs.SetResult(ret);
}
playfabMethod.Invoke( request, resultCallback, errorCallback, customData, extraHeaders );
return tcs.Task;
}
}
This (almost) works great like this:
var confirm = await PFAsyncWorks.Run<ConfirmPurchaseRequest, ConfirmPurchaseResult>(
PlayFabClientAPI.ConfirmPurchase, new ConfirmPurchaseRequest() {OrderId = "sd"} );
This works great, now I can call any of the PlayFab APIs (which all follow the same paradigm of request, PlayFabResponse. But I want to be able to handle the error callback properly. Does anyone know how I can cast the TOut object like I am trying to above so I can set the Error property?
I get the error "The type 'TOut' cannot be used as type parameter 'TResult' in the generic type or method 'PlayFabResult'. There is no implicit reference conversion from 'TOut' to 'PlayFab.SharedModels.PlayFabResultCommon'.
I have tried adding 'Where', but I still get issues
public static Task<TOut> Run<TIn, TOut>( PlayFabMethod<TIn,TOut> playfabMethod,
TIn request, object customData = null, Dictionary<string, string> extraHeaders = null )
where TIn : PlayFabRequestCommon where TOut : PlayFabResult<TOut>, new()
{
var tcs = new TaskCompletionSource<TOut>();
try
{
void resultCallback(TOut result)
{
tcs.TrySetResult(result);// TrySetResult(result);
}
void errorCallback(PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
TOut t = new TOut();
t.Error = error;
tcs.TrySetResult(t);//new TOut { Error = error });
}
playfabMethod.Invoke(request, resultCallback, errorCallback, customData, extraHeaders);
}
catch(Exception ex)
{
//tcs.TrySetException(new Exception(error.GenerateErrorReport())
tcs.TrySetException(ex);
}
return tcs.Task;
}
}
This version gives the error 'The type 'TOut' cannot be used as type parameter 'TResult' in the generic type or method 'PlayFabResult'. There is no implicit reference conversion from 'TOut' to 'PlayFab.SharedModels.PlayFabResultCommon'
Thanks for any help!
Well you do
if you think about it, it makes no sense since this basically means e.g. (I know the types make no sense - it is for clarification only)
=> What you want here is to limit the
TOuttoPlayFabResult<TResult>and then further limit thatTResulttoPlayFabResultCommon.You might rather need to wrap this further in another generic and do e.g.
However, I just imported the package and there are more issues with your approach:
There simply is no property/field
ErrorinPlayFabReslt<T>Not all the playfab results actually inherit from
PlayfabResult<TResult>!E.g.
LoginResultdirectly inherits fromPlayFabResultCommon, skipping thePlayfabResult<TResult>.Actually I didn't find ANY whatsoever ^^
For 2) you will need a different overload which actually kinda works like your first approach with only two generic types for the request and the result.
For 1) (and finally combining it all) I would simply introduce a wrapper type around the results like e.g.
and then change your methods to
Now you can do e.g.