I don't know what is wrong with my code because everything seems to be correct. Therefore I want to use App Center to find the issue. I have an iOS application and a similar Android application. I uploaded the applications to the Apple App Store and Google Play Store. The applications are in alpha/test mode in both stores. After that, I installed the applications on my iOS and Android device and App Center logged the stack traces after the exception happened but I still cannot find the issue because the stack traces are not detailed enough.
On Android, PlayFab receipt validation always fails and I get this exception
System.NullReferenceException(Object reference not set to an instance of an object.).
On iOS, I think PlayFab receipt validation never fails but I sometimes get the same exception
System.NullReferenceException(Object reference not set to an instance of an object.).
The variables PurchaseditemCurrencyCode, PurchaseditemPurchasePrice, SignedData and Signature are not null and I think that they have the correct values because I can see in my Google Play account that the in-app product purchase is successful, but in the PlayFab Game Manager receipt validation fails.
PlayFab iOS and Android receipt validation
My values on Android:
PurchaseditemCurrencyCode = "EUR",
PurchaseditemPurchasePrice = 229,
SignedData = "{\"orderId\":..." and Signature = "eMU3xCYDD4L..."
My Android stack trace:
{
"length": 0,
"offset": 0,
"id": "63783019-f17b-47e1-8865-4e6290f43fbd",
"exception": {
"type": "System.NullReferenceException",
"message": "Object reference not set to an instance of an object",
"stackTrace": " at ggdgdgd.Android.Game1.ValidateAndroidReceiptAsync (System.String purchaseditemcurrencycode, System.Int32 purchaseditempurchaseprice, System.String signeddata, System.String signature) [0x000c3] in <11446e8c56cb4e78a270889c3b890601>:0 \n at ggdgdgd.Android.Game1.DoValidateAndroidReceiptAsync (System.String purchaseditemcurrencycode, System.Int32 purchaseditempurchaseprice, System.String signeddata, System.String signature) [0x0007a] in <11446e8c56cb4e78a270889c3b890601>:0 \n at ggdgdgd.Android.Game1.CheckPurchase (System.String productId) [0x0030a] in <11446e8c56cb4e78a270889c3b890601>:0 ",
"wrapperSdkName": "appcenter.xamarin"
},
My values on iOS:
PurchaseditemCurrencyCode = "EUR",
PurchaseditemPurchasePrice = 229,
SignedData = "MllVSAYJK..."
My iOS stack trace:
{
"length": 0,
"offset": 0,
"id": "71aef385-bb48-44d7-b59d-a53368c72c66",
"exception": {
"type": "System.NullReferenceException",
"message": "Object reference not set to an instance of an object",
"stackTrace": " at InapppurchaseTest.iOS.Game1.ValidateIOSReceiptAsync (System.String purchaseditemcurrencycode, System.Int32 purchaseditempurchaseprice, System.String signeddata, System.String signature) <0x1047fa730 + 0x002dc> in <f7cd0204315c470baa0e7963fe272e8d#b28b8328fb987f6bc8a6ed35dda86f7b>:0 \n at InapppurchaseTest.iOS.Game1.DoValidateIOSReceiptAsync (System.String purchaseditemcurrencycode, System.Int32 purchaseditempurchaseprice, System.String signeddata, System.String signature) <0x1047fa4e0 + 0x00183> in <f7cd0204315c470baa0e7963fe272e8d#b28b8328fb987f6bc8a6ed35dda86f7b>:0 \n at InapppurchaseTest.iOS.Game1.CheckPurchase (System.String productId) <0x1047f7a00 + 0x0041f> in <f7cd0204315c470baa0e7963fe272e8d#b28b8328fb987f6bc8a6ed35dda86f7b>:0 ",
"wrapperSdkName": "appcenter.xamarin"
},
It's impossible for me to find the issue with the current stack traces. Is it possible to get a more detailed stack trace in Visual Studio App Center?
I think that there is something wrong with PlayFabClientAPI.ValidateIOSReceiptAsync and PlayFabClientAPI.ValidateGooglePlayPurchaseAsync but I don't know what.
My Android and iOS code:
string SignedData = "", Signature = "", PurchaseditemCurrencyCode = "", PurchaseMessage = "";
int PurchaseditemPurchasePrice;
async void CheckPurchase(string productId)
{
bool purchaseIsSuccessful = await PurchaseItem(productId, "");
if (purchaseIsSuccessful == true)
{
try
{
if (Device.RuntimePlatform == Device.iOS)
{
if (productId == "Consumable11")
await DoValidateIOSReceiptAsync(PurchaseditemCurrencyCode, PurchaseditemPurchasePrice, SignedData);
}
else
{
if (Device.RuntimePlatform == Device.Android)
{
if (productId == "Consumable11")
await DoValidateAndroidReceiptAsync(PurchaseditemCurrencyCode, PurchaseditemPurchasePrice, SignedData, Signature);
}
}
}
catch (NullReferenceException ex)
{
Crashes.TrackError(ex);
}
}
else
{
PurchaseMessage = "Product could not be purchased";
}
}
public async Task DoValidateAndroidReceiptAsync(string purchaseditemcurrencycode, int purchaseditempurchaseprice, string signeddata, string signature)
{
await ValidateAndroidReceiptAsync(purchaseditemcurrencycode, purchaseditempurchaseprice, signeddata, signature);
}
private async Task ValidateAndroidReceiptAsync(string purchaseditemcurrencycode, int purchaseditempurchaseprice, string signeddata, string signature)
{
var result = await PlayFabClientAPI.ValidateGooglePlayPurchaseAsync(new ValidateGooglePlayPurchaseRequest()
{
CurrencyCode = purchaseditemcurrencycode,
PurchasePrice = (uint)purchaseditempurchaseprice,
ReceiptJson = signeddata,
Signature = signature
});
if (result.Error != null)
PlayFabMessage = "not successful");
else
PlayFabMessage = "successful");
}
public async Task DoValidateIOSReceiptAsync(string purchaseditemcurrencycode, int purchaseditempurchaseprice, string signeddata)
{
await ValidateIOSReceiptAsync(purchaseditemcurrencycode, purchaseditempurchaseprice, signeddata);
}
private async Task ValidateIOSReceiptAsync(string purchaseditemcurrencycode, int purchaseditempurchaseprice, string signeddata)
{
var result = await PlayFabClientAPI.ValidateIOSReceiptAsync(new ValidateIOSReceiptRequest()
{
CurrencyCode = purchaseditemcurrencycode,
PurchasePrice = purchaseditempurchaseprice,
ReceiptData = signeddata
});
if (result.Error != null)
PlayFabMessage = "not successful");
else
PlayFabMessage = "successful");
}
It seems that your result can be null and you are not checking that. That's the only point of failure that I can see.
Related
I'm getting an error saying AuthDataResult does not contain ProfileChangeRequest() when i try to change the name of the user. I've to research online and read the firebase docs as well but no luck.
public static async Task Register(UIViewController thisView,
string inpName,
string inpEmail,
string inpPassword)
{
bool done = false;
AppDataClass.auth.CreateUser(inpEmail, inpPassword, (user, error) =>
{
if (error != null)
{
AlertShow.Alert(thisView, "Error",
"This went wrong: " + error.UserInfo.Description);
return;
}
UserProfileChangeRequest changeReq = user.ProfileChangeRequest;
changeReq.DisplayName = inpName;
changeReq.CommitChanges((profileError) =>
{
if (profileError != null)
{
AlertShow.Alert(thisView, "Error",
"Profile Error: " + profileError);
return;
}
done = true;
});
});
while (!done)
{
await Task.Delay(50);
}
}
If you read the document about method -signInWithEmail:password:completion:, the call back it retures is a FIRAuthDataResultCallback , the first parameter here is FIRAuthDataResult.
So the user should be:
user.user.ProfileChangeRequest
To make it clear:
public static async Task Register(UIViewController thisView,
string inpName,
string inpEmail,
string inpPassword)
{
bool done = false;
AppDataClass.auth.CreateUser(inpEmail, inpPassword, (authDataResult, error) =>
{
if (error != null)
{
AlertShow.Alert(thisView, "Error",
"This went wrong: " + error.UserInfo.Description);
return;
}
UserProfileChangeRequest changeReq = authDataResult.user.ProfileChangeRequest;
changeReq.DisplayName = inpName;
});
while (!done)
{
await Task.Delay(50);
}
}
Refer: ERROR is value of type 'AuthDataResult' has no member 'uid'
I'm building a C# HID library for the Trezor. It's working well. I'm able to get past the pin entry and retrieve my xpub, and I can get addresses. However, none of the addresses that are getting returned match any of my addresses in the Trezor wallet website.
You can see the HID doco here:
https://doc.satoshilabs.com/trezor-tech/api-workflows.html#passphrase-meta-workflow
This is not really a C# question. Rather, it's a general question for any Trezor HID developers. The big problem is that if I pass a HDNodePathType message as the Multisig property of the GetAddress method, I get the error "Can't encode address'". What do I need to pass with the GetAddress message to get a valid address?
Here is an Android repo:
https://github.com/trezor/trezor-android
This problem has now been resolved. Trezor.Net has a working example can be cloned here.
Here is the code for getting an address:
public async Task<string> GetAddressAsync(IAddressPath addressPath, bool isPublicKey, bool display, AddressType addressType, InputScriptType inputScriptType, string coinName)
{
try
{
var path = addressPath.ToArray();
if (isPublicKey)
{
var publicKey = await SendMessageAsync<PublicKey, GetPublicKey>(new GetPublicKey { CoinName = coinName, AddressNs = path, ShowDisplay = display, ScriptType = inputScriptType });
return publicKey.Xpub;
}
else
{
switch (addressType)
{
case AddressType.Bitcoin:
//Ultra hack to deal with a coin name change in Firmware Version 1.6.2
if ((Features.MajorVersion <= 1 && Features.MinorVersion < 6) && coinName == "Bgold")
{
coinName = "Bitcoin Gold";
}
return (await SendMessageAsync<Address, GetAddress>(new GetAddress { ShowDisplay = display, AddressNs = path, CoinName = coinName, ScriptType = inputScriptType })).address;
case AddressType.Ethereum:
var ethereumAddress = await SendMessageAsync<EthereumAddress, EthereumGetAddress>(new EthereumGetAddress { ShowDisplay = display, AddressNs = path });
var sb = new StringBuilder();
foreach (var b in ethereumAddress.Address)
{
sb.Append(b.ToString("X2").ToLower());
}
var hexString = sb.ToString();
return $"0x{hexString}";
default:
throw new NotImplementedException();
}
}
}
catch (Exception ex)
{
Logger?.Log("Error Getting Trezor Address", LogSection, ex, LogLevel.Error);
throw;
}
}
I am trying to make multiplayer game via Unity. I use sample asset to try and I get an error which is in below:
InvalidOperationException: Cannot override system-specified headers
UnityEngine.Networking.UnityWebRequest.SetRequestHeader (System.String name, System.String value) (at C:/buildslave/unity/build/artifacts/generated/common/modules/UnityWebRequest/WebRequestBindings.gen.cs:482)
UnityEngine.WWW..ctor (System.String url, System.Byte[] postData, System.Collections.Generic.Dictionary`2 headers) (at C:/buildslave/unity/build/Runtime/WebRequestWWW/UWRWWW.cs:62)
QuizMaker.Administrator.AdminAPI+<api_call>c__Iterator0`1[QuizMaker.Administrator.CheckConnectionResponse].MoveNext () (at Assets/QuizMaker/Scripts/Administrator/AdminAPI.cs:77)
UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Coroutines.cs:17)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
QuizMaker.Administrator.AdminAPI:CheckConnection(Callback`1) (at Assets/QuizMaker/Scripts/Administrator/AdminAPI.cs:227)
QuizMaker.Administrator.ServerSettingsUI:_updateServerStatus() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:147)
QuizMaker.Administrator.ServerSettingsUI:UpdateServerStatus() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:139)
QuizMaker.Administrator.ServerSettingsUI:Start() (at Assets/QuizMaker/Scripts/Administrator/Actions/ServerSettingsUI.cs:44)
My code:
// create a form for a post data
WWWForm form = new WWWForm();
// create a log string
var logString = string.Format("[API_REQ {0}]", action);
// add key val if data is empty
if (data.Length == 0)
{
data = new string[] { "key", "val" };
}
// add data from an data array to the form
for (int i = 0; i < data.Length - 1; i += 2)
{
// add key and value
form.AddField(data[i], data[i + 1]);
// add log
logString += string.Format(" [{0}: {1}]", data[i], data[i + 1]);
}
// it needs to be more secure
var date = DateTime.Now.ToString();
var headers = form.headers;
headers["Date"] = date;
headers["Order"] = Util.generateOrderString(data);
form.AddField("hash", Util.generateHash(data, date));
// print log string
print(logString);
// create www request
var www = new WWW(AppConfig.Instance.serverUrl + action, form.data, headers);
// wait for a response
yield return www;
// print log for each api call
print(string.Format("[API_RES {0}] [{1}]", action, www.text));
// parse a response
parseResponse(
string.IsNullOrEmpty(www.error),
www.text,
callback
);
When I delete date variable, error fixes however in this time I can't connect to server. My connection method is REST API. Thanks in advance.
I found the error. If anyone encountered same issue just change the following variable:
headers["Date"] = date;
to
headers["date"] = date;
This fixed my problem and saved my hours. Best regards.
I posted another post with the same issue. There is a simple Xamarin solution on github that you can download and run here. Please do.
It seems that calling System.Net.WebClient.DownloadDataTaskAsync in DEBUG mode on my Xperia Z3 (it works just fine with an emulator) the first time throws a NullReferenceException in System.Threading.Tasks.Task.Schedule:
public async Task<String> DownloadString(String url)
{
if (UseAuth)
webclient.Headers.Set("Authorization", TokenType + " " + AccessToken);
else if (!UseAuth && webclient.Headers["Authorization"] != null)
webclient.Headers.Remove("Authorization");
String response = "";
try
{
byte[] data = await webclient.DownloadDataTaskAsync(url);
response = Encoding.UTF8.GetString(data);
}
catch (WebException e)
{
response = new StreamReader (e.Response.GetResponseStream ()).ReadToEnd ();
ErrorResponse = JsonConvert.DeserializeObject<ErrorResponse> (response, settings);
if (ErrorResponse.Error.Status == 401 && ErrorResponse.Error.Message == "The access token expired") {
Console.WriteLine ("Error: " + ErrorResponse.Error.Status + " - " + ErrorResponse.Error.Message);
}
}
return response;
}
This code solves the problem, but I think it's a dirty fix and it doesn't really address the underlying problem.
public async Task<String> DownloadString(String url)
{
if (UseAuth)
webclient.Headers.Set("Authorization", TokenType + " " + AccessToken);
else if (!UseAuth && webclient.Headers["Authorization"] != null)
webclient.Headers.Remove("Authorization");
String response = "";
var failCount = 2;
for (int i = 0; i <= failCount; i++)
{
try
{
byte[] data = await webclient.DownloadDataTaskAsync(url);
response = Encoding.UTF8.GetString(data);
}
catch (WebException e)
{
if (i == failCount)
{
response = new StreamReader(e.Response.GetResponseStream()).ReadToEnd();
ErrorResponse = JsonConvert.DeserializeObject<ErrorResponse>(response, settings);
if (ErrorResponse.Error.Status == 401 && ErrorResponse.Error.Message == "The access token expired")
{
Console.WriteLine ("Error: " + ErrorResponse.Error.Status + " - " + ErrorResponse.Error.Message);
}
// Break out of the loop
break;
}
}
}
return response;
}
System.NullReferenceException: Object reference not set to an instance
of an object System.Threading.Tasks.Task.Schedule (Boolean
throwException) [0x00000] in :0 at System.Threading.Tasks.Task.Start
(System.Threading.Tasks.TaskScheduler scheduler) [0x00000] in :0 at
System.Threading.Tasks.TaskFactory.StartNew[WebRequest] (System.Func1
function, CancellationToken cancellationToken, TaskCreationOptions
creationOptions, System.Threading.Tasks.TaskScheduler scheduler)
[0x00000] in :0 at
System.Threading.Tasks.TaskFactory.StartNew[WebRequest] (System.Func1
function) [0x00000] in :0 at System.Net.WebClient.SetupRequestAsync
(System.Uri address) [0x00000] in :0 at
System.Net.WebClient+c__async0.MoveNext () [0x00000] in :0 --- End of
inner exception stack trace --- at
System.Net.WebClient+c__async0.MoveNext () [0x00000] in :0 --- End of
stack trace from previous location where exception was thrown --- at
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw ()
[0x00000] in :0 at
System.Runtime.CompilerServices.TaskAwaiter`1[System.Byte[]].GetResult
() [0x00000] in :0 at
SpotifyWebAPI.SpotifyWebAPIClass+c__async1A.MoveNext () [0x00133]
System.Net.WebException
Here is a screenshot from Xamarin Studio
The problem only happens with my device and DEBUG build mode. In RELEASE it works perfectly and on the emulator it works on both DEBUG and RELEASE.
As stated in the original post, calling System.WebClient.DownloadDataTaskAsync a second, third or Nth time all result in normal behaviour. It also only occur when I use a Spotify Android SDK Java binding project and call OpenLoginWindow() (which opens another activity, logs in and returns successfully) prior to calling DownloadDataTaskAsync.
I have a Xamarin.iOS app working that can successfully receive an APNS push notification when the app is running in the foreground. However, I also want to be able to handle the push notification when the app is running in the background, or not running at all.
This page on Xamarin appears to say that you can indeed do this:
http://developer.xamarin.com/guides/ios/application_fundamentals/backgrounding/part_3_ios_backgrounding_techniques/updating_an_application_in_the_background/
I am using the Remote Notifications strategy outlined above. So I implemented exactly what it said, including changing the info.plist to have Remote Notifications as a background operation. However, I do not get any calls either in the DidFinishedLaunching, or ReceivedRemoteNotification callbacks when the app is running in the background. When the app is running in the foreground, everything works just fine. Needless to say, it's super frustrating.
Has anyone else run into this problem?
Any help is much appreciated!
Here is the code I am using to register for the remote notifications:
public static void NotifyAppStart()
{
if (GetRemoteWipePreference())
{
if (Utils.CheckOsVersion(8, 0))
{
// iOS 8 version
UIUserNotificationSettings settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
// Only works on iOS 7 and earlier
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}
}
And here is the PushSharp code I am using to send the push notifications on the server side:
push.RegisterAppleService(new ApplePushChannelSettings(appleCert, GetApplePwd() ));
push.QueueNotification(new AppleNotification()
.ForDeviceToken( token ).WithContentAvailable(1)
.WithCustomItem(msg, device.device_local_guid) );
Side note: There are a few other questions on stack overflow about this general area, but none of them actually are about this issue.
if (UIDevice.CurrentDevice.CheckSystemVersion(8,0))
{
var settings = UIUserNotificationSettings.GetSettingsForTypes (UIUserNotificationType.Sound |
UIUserNotificationType.Alert | UIUserNotificationType.Badge, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings (settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications ();
}
else
{
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(UIRemoteNotificationType.Badge |
UIRemoteNotificationType.Sound | UIRemoteNotificationType.Alert);
}
also you need this
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
var oldDeviceToken = NSUserDefaults.StandardUserDefaults.StringForKey("PushDeviceToken");
var strFormat = new NSString("%#");
var dt = new NSString(MonoTouch.ObjCRuntime.Messaging.IntPtr_objc_msgSend_IntPtr_IntPtr(new MonoTouch.ObjCRuntime.Class("NSString").Handle, new MonoTouch.ObjCRuntime.Selector("stringWithFormat:").Handle, strFormat.Handle, deviceToken.Handle));
var newDeviceToken = dt.ToString().Replace("<", "").Replace(">", "").Replace(" ", "");
string sql;
if (string.IsNullOrEmpty(oldDeviceToken) || !deviceToken.Equals(newDeviceToken))
{
do someting
}
if (oldDeviceToken == newDeviceToken)
{
do someting
} else
{
do something
}
NSUserDefaults.StandardUserDefaults.SetString(newDeviceToken, "PushDeviceToken");
Console.WriteLine("Device Token: " + newDeviceToken);
}
if your program is foreground you can use like this
void processNotification(NSDictionary options, bool fromFinishedLaunching)
{
if (null != options && options.ContainsKey(new NSString("aps")))
{
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
string alert = string.Empty;
string sound = string.Empty;
int badge = -1;
if (aps.ContainsKey(new NSString("alert")))
alert = (aps[new NSString("alert")] as NSString).ToString();
if (aps.ContainsKey(new NSString("sound")))
sound = (aps[new NSString("sound")] as NSString).ToString();
if (aps.ContainsKey(new NSString("badge")))
{
string badgeStr = (aps[new NSString("badge")] as NSObject).ToString();
int.TryParse(badgeStr, out badge);
}
if (!fromFinishedLaunching)
{
if (badge >= 0)
UIApplication.SharedApplication.ApplicationIconBadgeNumber = badge;
if (sound == "xx.caf")
{
UIAlertView avAlert = new UIAlertView ("Dogrular App", alert, null, "Tamam", null);
avAlert.Show ();
}
if (sound == "yyy.caf")
{
UIAlertView avAlert = new UIAlertView ("Dogrular App", alert, null, "Tamam", null);
avAlert.Show ();
NSUrl request = new NSUrl("https://xxx.com");
UIApplication.SharedApplication.OpenUrl(request);
}
}
}
}
Is your json file consists of content-available boolean key is "false"?
Example:
{
"to": "GCM Registration ID",
"priority":"high",
"data": {
"notificationId":123,
"title":"Spot you!",
"message": "<font color=\"red\"><b>Test RED Bold text maybe ?</b></font><br/>Normal second line.<br/><br/>",
"photoUrl": "http://babymetal.net/wp-content/uploads/2015/07/ladybeard-e1427784688234.jpg",
"badge": 1
},
"dry_run" : false,
"time_to_live" : 4,
"content_available" : false
}
Above json return should be calling your method
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)