My app crashes sometimes when buying something with in-app purchase.
Most of the time it works fine but sometimes the app crashes without any error (I am testing in debug mode).
To set-up the in-app purchase (non-consumable), I used the following example:
https://github.com/conceptdev/xamarin-samples/tree/master/InAppPurchase/NonConsumables
The class which handles the in-app purchase looks like this:
public class InAppPurchaseManager : SKProductsRequestDelegate {
public static NSString InAppPurchaseManagerProductsFetchedNotification = new NSString("InAppPurchaseManagerProductsFetchedNotification");
public static NSString InAppPurchaseManagerTransactionFailedNotification = new NSString("InAppPurchaseManagerTransactionFailedNotification");
public static NSString InAppPurchaseManagerTransactionSucceededNotification = new NSString("InAppPurchaseManagerTransactionSucceededNotification");
public static NSString InAppPurchaseManagerRequestFailedNotification = new NSString("InAppPurchaseManagerRequestFailedNotification");
SKProductsRequest productsRequest;
CustomPaymentObserver theObserver;
SKProduct[] products;
public static NSAction Done {get;set;}
public InAppPurchaseManager ()
{
theObserver = new CustomPaymentObserver(this);
SKPaymentQueue.DefaultQueue.AddTransactionObserver(theObserver);
}
// received response to RequestProductData - with price,title,description info
public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
products = response.Products;
NSDictionary userInfo = null;
if (products.Length > 0) {
NSObject[] productIdsArray = new NSObject[response.Products.Length];
NSObject[] productsArray = new NSObject[response.Products.Length];
for (int i = 0; i < response.Products.Length; i++) {
productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier);
productsArray[i] = response.Products[i];
}
userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray);
}
NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerProductsFetchedNotification,this,userInfo);
foreach (string invalidProductId in response.InvalidProducts) {
Console.WriteLine("Invalid product id: " + invalidProductId );
}
}
// request multiple products at once
public void RequestProductData (List<string> productIds)
{
var array = new NSString[productIds.Count];
for (var i = 0; i < productIds.Count; i++) {
array[i] = new NSString(productIds[i]);
}
NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array);
//set up product request for in-app purchase
productsRequest = new SKProductsRequest(productIdentifiers);
productsRequest.Delegate = this; // SKProductsRequestDelegate.ReceivedResponse
productsRequest.Start();
Console.WriteLine ("BEREIKT");
}
// Verify that the iTunes account can make this purchase for this application
public bool CanMakePayments()
{
return SKPaymentQueue.CanMakePayments;
}
public void PurchaseProduct(string appStoreProductId)
{
Console.WriteLine("PurchaseProduct " + appStoreProductId);
SKPayment payment = SKPayment.PaymentWithProduct (appStoreProductId);
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
public void CompleteTransaction (SKPaymentTransaction transaction)
{
Console.WriteLine ("CompleteTransaction " + transaction.TransactionIdentifier);
var productId = transaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
//PhotoFilterManager.Purchase(productId);
UserDefaults.Purchase(productId);
FinishTransaction (transaction, true);
//Show Dialog
new UIAlertView("Succes", "De aankoop is gelukt." +
"\n Je kunt de gekozen categorieën nu spelen.", null, "OK", null).Show();
/*
if (ReceiptValidation.VerificationController.SharedInstance.VerifyPurchase (transaction)) {
Console.WriteLine ("Verified!");
// Register the purchase, so it is remembered for next time
PhotoFilterManager.Purchase(productId);
FinishTransaction (transaction, true);
} else {
Console.WriteLine ("NOT Verified :(");
FinishTransaction (transaction, false);
}
*/
}
public void RestoreTransaction (SKPaymentTransaction transaction)
{
// Restored Transactions always have an 'original transaction' attached
Console.WriteLine("RestoreTransaction " + transaction.TransactionIdentifier + "; OriginalTransaction " + transaction.OriginalTransaction.TransactionIdentifier);
var productId = transaction.OriginalTransaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
//PhotoFilterManager.Purchase(productId); // it's as though it was purchased again
UserDefaults.Purchase(productId);
FinishTransaction(transaction, true);
}
public void FailedTransaction (SKPaymentTransaction transaction)
{
//SKErrorPaymentCancelled == 2
if (transaction.Error.Code == 2) // user cancelled
Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
else // error!
Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
FinishTransaction(transaction,false);
//Show Dialog
new UIAlertView("Helaas", "De aankoop is mislukt." +
"\n Probeer het op een later tijdstip nogmaals a.u.b.", null, "OK", null).Show();
}
public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful)
{
Console.WriteLine("FinishTransaction " + wasSuccessful);
// remove the transaction from the payment queue.
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction); // THIS IS IMPORTANT - LET'S APPLE KNOW WE'RE DONE !!!!
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")});
if (wasSuccessful) {
// send out a notification that we’ve finished the transaction
NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerTransactionSucceededNotification,this,userInfo);
} else {
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerTransactionFailedNotification,this,userInfo);
}
}
}
/// <summary>
/// Probably could not connect to the App Store (network unavailable?)
/// </summary>
public override void RequestFailed (SKRequest request, NSError error)
{
Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")});
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerRequestFailedNotification,this,userInfo);
}
}
/// <summary>
/// Restore any transactions that occurred for this Apple ID, either on
/// this device or any other logged in with that account.
/// </summary>
public void Restore()
{
Console.WriteLine (" ** InAppPurchaseManager Restore()");
// theObserver will be notified of when the restored transactions start arriving <- AppStore
SKPaymentQueue.DefaultQueue.RestoreCompletedTransactions();
}
}
What could cause the crash?
FYI: I am using Xamarin iOS version 6.3.4.36 (beta). For now I use this beta version because it solves a problem I have with Game Center. The stable version of Xamarin does not solve this yet.
PS. I read that the example I used not implemented RECEIPT VERIFICATION. What does this mean and is this necessary to implement?
FIRST UPDATE:
Sometimes I get this error.
mono-rt: Stacktrace:
mono-rt: at <unknown> <0xffffffff>
mono-rt: at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff>
mono-rt: at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
mono-rt: at PP_IOS.Application.Main (string[]) [0x00001] in /Users/Mac01/Projects/PP/PP_IOS/Main.cs:19
mono-rt: at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff>
mono-rt:
Native stacktrace:
mono-rt:
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
And another time I get this error:
mono-rt: Stacktrace:
mono-rt: at <unknown> <0xffffffff>
mono-rt: at (wrapper managed-to-native) MonoTouch.ObjCRuntime.Messaging.void_objc_msgSend_IntPtr (intptr,intptr,intptr) <IL 0x00025, 0xffffffff>
mono-rt: at MonoTouch.StoreKit.SKPaymentQueue.AddPayment (MonoTouch.StoreKit.SKPayment) [0x0001c] in /Developer/MonoTouch/Source/monotouch/src/StoreKit/SKPaymentQueue.g.cs:107
mono-rt: at PP_IOS.InAppPurchaseManager.PurchaseProduct (string) [0x0001f] in /Users/Mac01/Projects/PP/PP_IOS/Utils/InAppPurchase/InAppPurchaseManager.cs:109
mono-rt: at PP_IOS.UpgradeScreen.<BuyCategoryArtistsAndSports>m__21 () [0x0003d] in /Users/Mac01/Projects/PP/PP_IOS/ControllersUniversal/UpgradeScreen.cs:171
mono-rt: at MonoTouch.Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in /Developer/MonoTouch/Source/monotouch/src/shared/Foundation/NSAction.cs:87
mono-rt: at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) <IL 0x0004e, 0xffffffff>
mono-rt: at <unknown> <0xffffffff>
mono-rt: at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <IL 0x0009f, 0xffffffff>
mono-rt: at MonoTouch.UIKit.UIApplication.Main (string[],string,string) [0x0004c] in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:38
mono-rt: at PP_IOS.Application.Main (string[]) [0x00001] in /Users/Mac01/Projects/PP/PP_IOS/Main.cs:19
mono-rt: at (wrapper runtime-invoke) <Module>.runtime_invoke_void_object (object,intptr,intptr,intptr) <IL 0x00050, 0xffffffff>
mono-rt:
Native stacktrace:
mono-rt:
=================================================================
Got a SIGSEGV while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
SECOND UPDATE
I just found the way the problem occurs. The buttons to buy and restore an in-app purchase are displayed in a modal view. It seems that the app crashes when I reopen the modal view and tap at the buy or restore button. So when I open the modal view for the first time and I tap at the buy and restore buttons it (most of the time) works fine. But when I reopen the modal view, and I tap at the buy or restore button, the app crashes with the error showed above.
Somebody familiar with this?
Problem solved!
I had to remove the TransactionObserver when closing the view.
public override void ViewWillDisappear (bool animated)
{
base.ViewWillDisappear (animated);
//Prevents crash when re-opening view
SKPaymentQueue.DefaultQueue.RemoveTransactionObserver (theObserver);
}
Ok, this is really dumb. I would go ahead and pull out a stamp and pen to get that strongly-worded letter to Tim Cook started.
I found from experience that SKProductsRequestDelegate.RequestFailed can occasionally return a null NSError. This would cause a null reference exception on the first line of your method. This is pretty terrible, and I'm not sure why it happens.
You might change your code to this:
public override void RequestFailed (SKRequest request, NSError error)
{
if (error == null)
Console.WriteLine("NSError is null!");
else
Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {error},new NSObject[] {new NSString("error")});
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName(InAppPurchaseManagerRequestFailedNotification,this,userInfo);
}
}
Make sure you account for it on the other end of the NSNotificationCenter, too.
BTW, I don't know what all that NSAutoreleasePool jazz is for--you should remove it. Did you get this from a really old MonoTouch example?
I have had the same issue also. I found that having
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
in the viewDidUnload does not work. It needs to be in viewWillDisappear. That appears to have fixed it for me.
Related
We just implemented a new push notification feature for our Xamarin.forms app using azure notification hub as provider. On iOS the notifications work perfectly fine when app is fresh installed on iOS device, i.e. no prior version of the app is installed on device. But when I try to upgrade the existing app on iOS device by installing the newer version on top, then I don't get any notifications.
When I debugged, I found that only during app upgrade scenario, the iOS app throws a UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.' Due to this exception the device doesn't get registered with Apple Push Notification Services and thus doesn't get any push notifications.
Below is the full exception with stacktrace:
UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.'
UIKit.UIKitThreadAccessException
Message=UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
Source=mscorlib
StackTrace:
at UIKit.UIApplication.EnsureUIThread () [0x00020] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIApplication.cs:96
at UIKit.UIGestureRecognizer..ctor (Foundation.NSObject target, System.IntPtr action) [0x00016] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIGestureRecognizer.g.cs:102
at UIKit.UIGestureRecognizer..ctor (System.IntPtr sel, UIKit.UIGestureRecognizer+Token token) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIGestureRecognizer.cs:66
at UIKit.UITapGestureRecognizer..ctor (System.Action`1[T] action) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIGestureRecognizer.cs:208
at Xamarin.Forms.Platform.iOS.EventTracker.CreateTapRecognizer (System.Int32 numTaps, System.Action`1[T] action, System.Int32 numFingers) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:462
at Xamarin.Forms.Platform.iOS.EventTracker.GetNativeRecognizer (Xamarin.Forms.IGestureRecognizer recognizer) [0x00049] in D:\a\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:258
at Xamarin.Forms.Platform.iOS.EventTracker.LoadRecognizers () [0x00042] in D:\a\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:572
at Xamarin.Forms.Platform.iOS.EventTracker.ModelGestureRecognizersOnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:624
at (wrapper delegate-invoke) <Module>.invoke_void_object_NotifyCollectionChangedEventArgs(object,System.Collections.Specialized.NotifyCollectionChangedEventArgs)
at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00018] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedAction action, System.Object item, System.Int32 index) [0x00009] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.ObservableCollection`1[T].InsertItem (System.Int32 index, T item) [0x0001a] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00020] in <cbddc4225b2f45f09f3a1d43a1268bc0>:0
at Xamarin.Forms.View.<.ctor>g__AddItems|14_1 (Xamarin.Forms.View+<>c__DisplayClass14_0& ) [0x00032] in D:\a\1\s\Xamarin.Forms.Core\View.cs:85
at Xamarin.Forms.View.<.ctor>b__14_0 (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) [0x0002f] in D:\a\1\s\Xamarin.Forms.Core\View.cs:101
at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00018] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedAction action, System.Object item, System.Int32 index) [0x00009] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.ObservableCollection`1[T].InsertItem (System.Int32 index, T item) [0x0001a] in <b912bfaf235d4ed8af62226c84967349>:0
at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00020] in <cbddc4225b2f45f09f3a1d43a1268bc0>:0
at SWAPA.MemberMobileApp.UI.Views.Login.CommonSetup () [0x002a1] in C:\MemberMobileApp\SWAPA.MemberMobileApp.UI\SWAPA.MemberMobileApp.UI\SWAPA.MemberMobileApp.UI\Views\Login.xaml.cs:369
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_1 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1037
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (System.Object state) [0x0000d] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1370
at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00071] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:968
at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:910
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1341
at System.Threading.ThreadPoolWorkQueue.Dispatch () [0x00074] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:899
at ObjCRuntime.Runtime.ThreadPoolDispatcher (System.Func`1[TResult] callback) [0x00006] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/ObjCRuntime/Runtime.cs:288
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00009] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1258
The only implementation that I have done in AppDelegate is to register the device with Apple Push Notification Services and then send that device registration id to main UI app to register the device with Azure notification hub.
Below is my code with some comments:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
//----Block needed for App Center Testing-----
#if ENABLE_TEST_CLOUD
//Xamarin.Calabash.Start();
#endif
//----Block needed for App Center Testing-----
new SfCalendarRenderer();
LoadApplication(new App());
SfImageEditorRenderer.Init();
SfListViewRenderer.Init();
SfCheckBoxRenderer.Init();
SfPdfDocumentViewRenderer.Init();
SfPickerRenderer.Init();
Syncfusion.SfChart.XForms.iOS.Renderers.SfChartRenderer.Init();
new Syncfusion.SfAutoComplete.XForms.iOS.SfAutoCompleteRenderer();
UITabBar.Appearance.SelectedImageTintColor = UIColor.FromRGB(213, 84, 39);
// Color of the tabbar background:
UITabBar.Appearance.BarTintColor = UIColor.LightGray;
// Color of the selected tab text color:
UITabBarItem.Appearance.SetTitleTextAttributes(
new UITextAttributes()
{
TextColor = UIColor.FromRGB(213, 84, 39)
},
UIControlState.Selected);
// Color of the unselected tab icon & text:
UITabBarItem.Appearance.SetTitleTextAttributes(
new UITextAttributes()
{
TextColor = UIColor.FromRGB(65, 64, 66)
},
UIControlState.Normal);
base.FinishedLaunching(app, options);
/*Register device with Apple Push Notification Service*/
RegisterForRemoteNotifications();
if (options != null && options.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey))
{
NSDictionary userInfo = (NSDictionary)options[UIApplication.LaunchOptionsRemoteNotificationKey];
if (userInfo != null)
{
PushNotifications.IsNotifictaionClick_AppInActive = true;
// To remove all delivered notifications
RemoveAllDeliveredPushNotifications();
}
}
return true;
}
//Register device with Apple Push Notification Services
void RegisterForRemoteNotifications()
{
//register for remote notifications based on system version
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert |
UNAuthorizationOptions.Sound |
UNAuthorizationOptions.Sound,
(granted, error) =>
{
if (granted)
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications); // Ask user for permission to receive notifications on device.
});
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
UNUserNotificationCenter.Current.Delegate = this;
}
//Send the PNS handle to UI app for device installation with Azure Notificaiton Hub.
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
//Format the pns handle before registering with Azure notification hub.
PushNotifications.PNSHandle = deviceToken.DebugDescription.Replace("<", string.Empty)
.Replace(">", string.Empty)
.Replace(" ", string.Empty)
.ToUpper();
}
// Process notification when received.
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo);
}
void ProcessNotification(NSDictionary options)
{
// make sure we have a payload
if (options != null && options.ContainsKey(new NSString("aps")))
{
// get the APS dictionary and extract message payload. Message JSON will be converted
// into a NSDictionary so more complex payloads may require more processing
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
NSDictionary alertMessage = aps.ObjectForKey(new NSString("alert")) as NSDictionary;
//Extract notification content
NSString messageKey = new NSString("body");
NSString titleKey = new NSString("title");
string messageBody = alertMessage.ContainsKey(messageKey) ? alertMessage[titleKey].ToString() : string.Empty;
string title = alertMessage.ContainsKey(titleKey) ? alertMessage[titleKey].ToString() : string.Empty;
if (!string.IsNullOrWhiteSpace(title) || !string.IsNullOrWhiteSpace(messageBody))
{
var content = new UNMutableNotificationContent();
content.Title = title;
content.Body = messageBody;
var requestID = title;
var request = UNNotificationRequest.FromIdentifier(requestID, content, null);
UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
{
if (err != null)
{
// TODO: log error messages in error data table.
Debug.WriteLine($"Received request to process notification but something went wrong.");
}
});
}
}
else
{
// TODO: log error messages in error data table.
Debug.WriteLine($"Received request to process notification but there was no payload.");
}
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
}
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action
completionHandler)
{
completionHandler();
/*Logic to open NotificationInboxPage onclick of notification when app was active */
if (!App.IsAppLaunching)
{
var modalStack = App.Current.MainPage.Navigation.ModalStack;
if (modalStack.Count > 0 && modalStack.Last().ToString().Contains("NotificationInbox"))
App.Current.MainPage.Navigation.PopModalAsync();
App.Current.MainPage.Navigation.PushModalAsync(new NotificationInboxPage());
}
App.IsAppLaunching = false;
//To remove all delivered notifications
RemoveAllDeliveredPushNotifications();
}
private void RemoveAllDeliveredPushNotifications()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
UNUserNotificationCenter.Current.RemoveAllDeliveredNotifications();
}
else
{
UIApplication.SharedApplication.CancelAllLocalNotifications();
}
}
}
The exception says I am calling UIKit method that should only be invoked from UI thread. The only action I am calling is InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications); which I am already invoking on main thread. So I am not sure what other function is the exception talking about. And why is it complaining only when I am upgrading the app and not when I am fresh installing the app.
I checked this post here but didn't get much help. At this point I am completely stuck due to this and really need some help on resolving the issue. Any idea what I might be missing?
Thanks in advance.
As you can read from the stack trace provided by you in the questions, the error is actually occuring on line 369 of your ‘Login.xaml.cs’ file.
If you place an "Exception catchpoint" and it should tell you exactly what line is causing that issue. Placing a try-catch around it should fix the crash, but try to to resolve the issue as well since exceptions in general are bad.
Besides that, you can also to use DispatchAsync instead
I am using the Firebase Unity SDK for authentication with FB and Google.
When trying it out on iOS FB works fine, but whenever I login with Google it works the first time, but when I relaunch the application and try to use the Google login again it causes an internal Firebase error.
Here's the printout I get :
SignInWithCredentialAsync encountered an error: System.AggregateException: Exception of type 'System.AggregateException' was thrown.
-----------------
Encountered a FirebaseException:An internal error has occurred, print and inspect the error details for more information.
Valley.DatabaseHandler:<RequestLogin>m__0(Task`1)
System.Threading.Tasks.<ContinueWith>c__AnonStorey0:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey2:<>m__0(Task)
System.Threading.Tasks.<ContinueWith>c__AnonStorey1:<>m__0()
System.Threading.Tasks.Task:<immediateExecutor>m__1(Action)
System.Threading.Tasks.Task`1:RunContinuations()
System.Threading.Tasks.Task`1:TrySetException(AggregateException)
System.Threading.Tasks.TaskCompletionSource`1:SetException(Exception)
Firebase.Auth.<GetTask>c__AnonStorey0:<>m__0()
Firebase.Auth.Future_User:SWIG_CompletionDispatcher(Int32)
Firebase.AppUtil:PollCallbacks()
Firebase.Platform.FirebaseMonoBehaviour:Update()
My RequestLogin() function looks like this :
public void RequestLogin(string id, string token, bool useId)
{
Credential credential;
if (useId)
{
credential = GoogleAuthProvider.GetCredential(id, token);
}
else
{
credential = FacebookAuthProvider.GetCredential(token);
}
if (credential != null)
{
auth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
AggregateException ex = task.Exception as AggregateException;
if (ex != null)
{
FirebaseException fbEx = null;
foreach (Exception e in ex.InnerExceptions)
{
fbEx = e as FirebaseException;
if (fbEx != null)
{
Debug.LogError("Encountered a FirebaseException:" + fbEx.Message);
fbEx = null;
}
}
}
return;
}
if (task.IsCompleted)
{
FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
_user = auth.CurrentUser;
_gameController.ActivateScene(GameController.Scene.GAME);
}
});
}
}
As you can see I'm not finding any more details in the exceptions.
Anyone know how to solve this? And why it only happens from the second launch and onwards and not on the first login with Google?
Any help would be much appreciated!
Edit:
At times I also get this which might be related:
System.ObjectDisposedException: The object was used after being disposed.
at System.Net.Sockets.Socket.get_Available () [0x00000] in <filename unknown>:0
at System.Net.Sockets.NetworkStream.BeginWrite (System.Byte[] buffer, Int32 offset, Int32 size, System.AsyncCallback callback, System.Object state) [0x00000] in <filename unknown>:0
at Mono.Security.Protocol.Tls.RecordProtocol.SendAlert (Mono.Security.Protocol.Tls.Alert alert) [0x00000] in <filename unknown>:0
at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0
I have Xamarin Forms app supporting iOS and Android. It works correctly on Android in release mode. On iOS, it doesn't work in release mode but works in Debug mode. It's crashing when clicked on a button which calls few functions. I have attached error log below. The strange thing is, it works on Simulator in release mode.
Error log:
Incident Identifier: 391C564C-028D-4084-BC4A-21ED49BB1F25
CrashReporter Key: 30B4418D-AA04-4575-97A3-97A9F491C62D
Hardware Model: iPhone7,2
Process: TestApp.iOS [600]
Path: /var/containers/Bundle/Application/7F4E5392-9BEA-4578-9E23-02112B61EB96/TestApp.iOS.app/TestApp.iOS
Identifier: uk.co.company.testapp
Version: 0.0.1 (1.0)
Code Type: ARM-64
Parent Process: ??? [1]
Date/Time: 2018-01-22T13:02:31Z
Launch Time: 2018-01-22T13:02:23Z
OS Version: iPhone OS 9.3.2 (13F69)
Report Version: 104-Xamarin
Exception Type: SIGABRT
Exception Codes: #0 at 0x180da811c
Crashed Thread: 0
Application Specific Information:
*** Terminating app due to uncaught exception 'System.ArgumentNullException', reason: 'System.ArgumentNullException: Value cannot be null.'
Xamarin Exception Stack:
Parameter name: method
at System.Linq.Expressions.Expression.Call (System.Linq.Expressions.Expression instance, System.Reflection.MethodInfo method, System.Collections.Generic.IEnumerable`1[T] arguments) [0x00111] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs:1239
at System.Linq.Expressions.Expression.Call (System.Linq.Expressions.Expression instance, System.Reflection.MethodInfo method, System.Linq.Expressions.Expression[] arguments) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs:1046
at System.Linq.Expressions.Expression.Call (System.Reflection.MethodInfo method, System.Linq.Expressions.Expression[] arguments) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/MethodCallExpression.cs:1001
at System.Dynamic.ExpandoObject+MetaExpando.BindSetMember (System.Dynamic.SetMemberBinder binder, System.Dynamic.DynamicMetaObject value) [0x00033] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/ExpandoObject.cs:863
at System.Dynamic.SetMemberBinder.Bind (System.Dynamic.DynamicMetaObject target, System.Dynamic.DynamicMetaObject[] args) [0x00035] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/SetMemberBinder.cs:57
at System.Dynamic.DynamicMetaObjectBinder.Bind (System.Object[] args, System.Collections.ObjectModel.ReadOnlyCollection`1[T] parameters, System.Linq.Expressions.LabelTarget returnLabel) [0x000c6] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Dynamic/DynamicMetaObjectBinder.cs:90
at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T] (System.Runtime.CompilerServices.CallSite`1[T] site, System.Object[] args) [0x00019] in <8bc31b0df50a4d32b3f1d5af764165ad>:0
at System.Runtime.CompilerServices.CallSiteOps.Bind[T] (System.Runtime.CompilerServices.CallSiteBinder binder, System.Runtime.CompilerServices.CallSite`1[T] site, System.Object[] args) [0x00000] in <8bc31b0df50a4d32b3f1d5af764165ad>:0
at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&)
at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00032] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:305
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:152
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw (System.Exception source) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:156
at System.Linq.Expressions.Interpreter.ExceptionHelpers.UnwrapAndRethrow (System.Reflection.TargetInvocationException exception) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Utilities.cs:172
at System.Linq.Expressions.Interpreter.MethodInfoCallInstruction.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00035] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/CallInstruction.cs:327
at System.Linq.Expressions.Interpreter.Interpreter.Run (System.Linq.Expressions.Interpreter.InterpretedFrame frame) [0x00015] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/Interpreter/Interpreter.cs:63
at System.Linq.Expressions.Interpreter.LightLambda.Run3[T0,T1,T2,TRet] (T0 arg0, T1 arg1, T2 arg2) [0x00038] in <8bc31b0df50a4d32b3f1d5af764165ad>:0
at TestApp.LoginPage+<onLoginClicked>d__1.MoveNext () [0x00205] in <42b3f78eef3b4fad8ca201e1a739e68b>:0
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:152
at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__6_0 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.6.1.3/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1018
at UIKit.UIKitSynchronizationContext+<Post>c__AnonStorey0.<>m__0 () [0x00000] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIKitSynchronizationContext.cs:24
at Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/Foundation/NSAction.cs:163
at (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:79
at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x00038] in /Users/builder/data/lanes/5665/f70a1348/source/xamarin-macios/src/UIKit/UIApplication.cs:63
at TestApp.iOS.Application.Main (System.String[] args) [0x00000] in <52e34ea9026340479b8dc6a53c640f1e>:0
EDIT:
I have an interesting finding. I'm calling onLoginClicked function on the touch of a button, which then calls PostData function which makes a REST Call to send data to the server. Whenever the PostData function is called, app crashes.
If I move all the code from PostData function to onLoginClicked function, it works!
Please find additional information below:
I tried cleaning and rebuilding the solution but, it didn't work.
There is no point in putting break points on each line in the PostData method as the app is crashing only in release mode, but works in debug mode.
The app crashes in onLoginClicked method at a line where PostData function is called i.e.
string responseData = await PostData(user, "auth/login");
When I tried putting some alerts in PostData function to see what
point it reaches but found that it doesn't even hit the very first
line in that method. This is very strange.
Code:
async void onLoginClicked(object sender, System.EventArgs e)
{
string error = "";
if (txtEmail.Text == null || txtEmail.Text == "")
{
error += Constants.ERR_BLANK_EMAIL + "\r\n";
}
else if (txtEmail.Text.Length < 6)
{
error += Constants.ERR_EMAIL_LENGTH + "\r\n";
}
else if (!Common.isEmailValid(txtEmail.Text))
{
error += Constants.ERR_EMAIL_VALIDATION + "\r\n";
}
if (txtPassword.Text == null || txtPassword.Text == "")
{
error += Constants.ERR_BLANK_PASSWORD;
}
if (error != "")
{
lblError.Text = error;
errorContainer.IsVisible = true;
return;
}
else
{
errorContainer.IsVisible = false;
}
if (CrossConnectivity.Current.IsConnected)
{
var loadingPage = new LoadingPage();
await Navigation.PushPopupAsync(loadingPage);
var device_token = App.channelId;
dynamic user = new System.Dynamic.ExpandoObject();
user.email = txtEmail.Text;
user.user_type = "b2b";
user.device_token = device_token;
user.password = txtPassword.Text;
if (Device.RuntimePlatform == Device.iOS)
{
user.device_type = "ios";
}else{
user.device_type = "android";
}
string responseData = await PostData(user, "auth/login");
dynamic responseObj = JObject.Parse(responseData);
if (responseObj["error_code"].Value == 0)
{
var data = responseObj["data"];
Constants.user = Common.GetUserObject(data.ToString());
Helpers.Settings.Password = "" + txtPassword.Text;
Helpers.Settings.UserId = "" + Constants.user.userId;
Helpers.Settings.accessToken = ""+data.access_token;
Helpers.Settings.Email = "" + txtEmail.Text;
var outletResp = responseObj["outlet_details"];
var outlet_count = outletResp.Count;
if(outlet_count>0)
{
Constants.outlets = new List<Outlet>();
foreach(var store in outletResp){
Outlet shop = new Outlet();
shop.access_token = store["access_token"];
shop.email = store["email"];
shop.user_id = store["id"];
shop.master_id = store["master_id"];
shop.outlet_id = store["outlet_id"];
shop.post_code = store["post_code"];
shop.profile_pic = store["profile_pic"];
shop.retailer_id = store["retailer_id"];
shop.retailer_image = store["retailer_image"];
shop.store_name = store["store_name"];
shop.tutorial_pricing_tool = int.Parse(""+store["tutorial_pricing_tool"]);
Constants.outlets.Add(shop);
}
}
var isPinSet = Helpers.Settings.PinSet;
await Navigation.RemovePopupPageAsync(loadingPage);
if (responseObj.data.pin==0 || !isPinSet){
App.Current.MainPage = new NavigationPage(new SetPinPage(false));
}
else{
App.Current.MainPage = new NavigationPage(new StoreListPage());
}
}
else
{
await Navigation.RemovePopupPageAsync(loadingPage);
if (responseObj["error_code"].Value == 1)
lblError.Text = responseObj["error"];
else
lblError.Text = responseObj["message"];
errorContainer.IsVisible = true;
}
}
else
{
await DisplayAlert(Constants.APP_TITLE, "Please check your internet connection.", "OK");
}
}
async public Task<string> PostData(System.Dynamic.ExpandoObject args, string actionUrl)
{
string responseData = "";
HttpClient client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
var param = Constants.JWT ? GetJWTToken(args, actionUrl) : JsonConvert.SerializeObject(args);
var content = new StringContent(param, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("Authorization", param);
client.DefaultRequestHeaders.Add("X-Device-Type", "android");
client.DefaultRequestHeaders.Add("X-APP-Type", "b2b");
client.DefaultRequestHeaders.Add("X-APP-Platform", "xamarin");
HttpResponseMessage response = null;
response = await client.PostAsync(Constants.BaseUrl + actionUrl, content);
if (response.IsSuccessStatusCode)
{
response.EnsureSuccessStatusCode();
}
responseData = await response.Content.ReadAsStringAsync();
if (Constants.JWT)
{
var resp = JObject.Parse(responseData);
var respToken = resp["token"];
var rtokenStr = respToken.ToObject<String>();
var respPayloadStr = rtokenStr.Split('.');
byte[] data = JWTLibrary.Converter.Base64UrlDecode(respPayloadStr[1]);
responseData = JWTLibrary.Converter.GetStringFromBytes(data);
}
return responseData;
}
private string GetJWTToken(System.Dynamic.ExpandoObject args, string actionUrl)
{
var dt = DateTime.Now;
var ticks = dt.Ticks;
var currentSec = ticks / TimeSpan.TicksPerSecond;
var newEndTime = currentSec + 60;
Object payload = new Dictionary<string, object>()
{
{ "iat", currentSec },
{ "nbf", currentSec },
{ "exp", newEndTime },
{ "iss", actionUrl },
{ "jti", "" },
{ "bat_data", args }
};
var secret_key = "key";
dynamic n_header = new System.Dynamic.ExpandoObject();
n_header.typ = "jwt";
n_header.alg = "HS256";
byte[] headerBytes = JWTLibrary.Converter.GetBytesFromString(JsonConvert.SerializeObject(n_header));
byte[] payloadBytes = JWTLibrary.Converter.GetBytesFromString(JsonConvert.SerializeObject(payload));
var enc_header = JWTLibrary.Converter.Base64UrlEncode(headerBytes);
var enc_payload = JWTLibrary.Converter.Base64UrlEncode(payloadBytes);
string jwt_token = enc_header + "." + enc_payload;
var sh_h256 = JWTLibrary.Converter.CreateToken(jwt_token, secret_key);
var jwt_enc_signature = JWTLibrary.Converter.Base64UrlEncode(sh_h256);
jwt_token = jwt_token + "." + jwt_enc_signature;
return jwt_token;
}
I'm afraid the problem was caused by dynamic keyword.
The DynamicMethod class is a part of the set of runtime code generation features that reside with System.Reflection.Emit. However,System.Reflection.Emit API is not available in iOS.
Detail refer to Why can’t I use the “dynamic” C# keyword in Xamarin.iOS?
I am trying to build watch kit application and connect it with my iOS app, using xamarin framework.In HandleWatchKitExtensionRequest I use MarketRequestHandler class in order to bring data from API and display it on watchkit.
My Error is:
"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}
Class: {ObjCRuntime.Class}
ClassHandle (Foundation.NSError): 0xf09c10
ClassHandle (Foundation.NSObject): 0xf09c10
Code: 2
DebugDescription: "Error Domain=com.apple.watchkit.errors Code=2 \"The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]\" UserInfo={NSLocalizedDescription=The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}"
This is my code:
public override void HandleWatchKitExtensionRequest
(UIApplication application, NSDictionary userInfo, Action<NSDictionary> reply) {
var json = new JsonParams ();
var id="";
nint taskID = UIApplication.SharedApplication.BeginBackgroundTask (() => {
});
new Task (() =>{
MarketRequestHandler mrk = new MarketRequestHandler();
json.ckeys = new string[]{"P"};
json.ids = new string[0];
json.fields = new string[]{
"LastDealTime",
"LastDealDate"
};
json.index = 0;
json.count = 300;
var jsonStr = JsonConvert.SerializeObject (json);
mrk.HandleRequest("market.get",jsonStr, (cb) =>{
id = json.ids[0];
reply (new NSDictionary (
"index", NSNumber.FromInt32 ((int)json.index),
"count", NSNumber.FromInt32((int)json.count),
"rzf", new NSString(id)
));
});
UIApplication.SharedApplication.EndBackgroundTask(taskID);
}).Start ();
}
Watch interface
public override void Awake (NSObject context)
{
// Configure interface objects here.
base.Awake (context);
Console.WriteLine ("{0} awake with context", this);
WKInterfaceController.OpenParentApplication (new NSDictionary (), (replyInfo, error) => {
if(error != null) {
Console.WriteLine (error);
return;
}
Console.WriteLine ("parent app responded");
// do something with replyInfo[] dictionary
Label.SetText ( replyInfo.Keys[0].ToString() + " " + replyInfo.Values[0].ToString());
label2.SetText(replyInfo.Keys[1].ToString() + " " + replyInfo.Values[1].ToString());
label3.SetText(replyInfo.Keys[2].ToString() + " " + replyInfo.Values[2].ToString());
});
}
Looking at your code, I would guess that id = json.ids[0]; throws an IndexOutOfRangeException since previously you set it to an empty array. That's why reply is never called. Perhaps you wanted to use cb instead of json in the callback handler.
So I have been trying to setup in app purchase within my application and encountered a problem which I don't know how to solve. Im working with xamarin, and I followed their In app purchase guide on how to buy consumables products.
Everything was going great until I tried to get the transaction receipt returned by apple. Everytime I access this property from the SKPaymentTransaction object (anywhere in my project), I get this error which according to some people is a memory leak. This just happens whenever I access this property (SKPaymentTransaction.TransactionReceipt).
The error:
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
My code looks a lot like the one found on the guide I previously told you about. First of all I created a in app purchase manager which is the one in charge of making requests to apple in order to obtain product information (this works great), and update my UI whenever a transaction is succeeded or failed:
public class InAppPurchaseManager : SKProductsRequestDelegate
{
IMobileServiceTable receiptTable = AppDelegate.MobileService.GetTable("Receipt");
public InAppPurchaseManager ()
{
SKPaymentQueue.DefaultQueue.AddTransactionObserver (new TransactionObserver(this));
}
public void RequestProductData (List<NSString> productIds)
{
var array = new NSString[productIds.Count];
for (var i = 0; i < productIds.Count; i++) {
array[i] = productIds[i];
}
NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array);
var productsRequest = new SKProductsRequest(productIdentifiers);
productsRequest.Delegate = this; // for SKProductsRequestDelegate.ReceivedResponse
productsRequest.Start();
}
public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
SKProduct[] products = response.Products;
NSDictionary userInfo = null;
if (products.Length > 0) {
NSObject[] productIdsArray = new NSObject[response.Products.Length];
NSObject[] productsArray = new NSObject[response.Products.Length];
for (int i = 0; i < response.Products.Length; i++) {
productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier);
productsArray[i] = response.Products[i];
}
userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray);
}
NSNotificationCenter.DefaultCenter.PostNotificationName ("InAppPurchaseManagerProductsFetchedNotification", this, userInfo);
}
public override void RequestFailed (SKRequest request, NSError error)
{
Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
}
public void PuchaseProduct (SKProduct product)
{
SKPayment payment = SKPayment.PaymentWithProduct (product);
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
public void CompleteTransaction (SKPaymentTransaction transaction)
{
var productId = transaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
FinishTransaction(transaction, true);
}
public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful)
{
// remove the transaction from the payment queue.
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")});
if (wasSuccessful) { NSNotificationCenter.DefaultCenter.PostNotificationName (new NSString("InAppPurchaseManagerTransactionSuccedeedNotification"), this, userInfo);
} else {
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName (new NSString("InAppPurchaseManagerTransacionFailedNotification"), this, userInfo);
}
}
}
public void FailedTransaction (SKPaymentTransaction transaction)
{
if (transaction.Error.Code == 2) // user cancelled
Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
else // error!
Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
FinishTransaction(transaction,false);
}
}}
On the method FinishTransaction I would like to insert the returned receipt in my server (before insert, of course verify it against apple servers) if the transaction was successful. So at this point I need to access SKPaymentTransaction.TransactionReceipt, encode this receipt to base64, and send it to my server. This just does not work and I dont know why.
My transaction observer:
public class TransactionObserver : SKPaymentTransactionObserver
{
private InAppPurchaseManager iap;
public TransactionObserver (InAppPurchaseManager manager) : base()
{
iap = manager;
}
public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
foreach (SKPaymentTransaction transaction in transactions)
{
switch (transaction.TransactionState)
{
case SKPaymentTransactionState.Purchased:
iap.CompleteTransaction (transaction);
break;
case SKPaymentTransactionState.Failed:
iap.FailedTransaction(transaction);
break;
default:
break;
}
}
}
}
So, another info I can give you is that my project compiles, and when it tries to deploy the app to my device, it crashes.
Also, I am on the xamarin beta channel since I am already using async and await.
So please if you see anything wrong let me know.
UPDATE
Everytime i clean the project it works, and stops giving that error!!! I dont know why this is behaving like this!
This is a known bug.
There is also a workaround in the bug report: add "-f" to the additional mtouch arguments in the project's iOS Build options page.