Xamarin.iOS Push Notifications HTTP Exception, the controller for push/installations/installation_id

Cdn_EuroCdn_Euro Member ✭✭✭

I know there can be problems when using the same device for production and development alternatively when it comes to Push Notifications and Notifications Hubs with Azure.

The way I have set things up is like this for iOS:

  1. I have one Notification Hub in Azure set for Production.
  2. Another Notification Hub in Azure set for Development.
  3. When I work with the app in development, in AppDelegate.cs I just register with the development hub such as this:

    var client = new MobileServiceClient(AppConstants.AZURE_DEVELOPMENT_WEB_API_URL);

  4. When the app is ready for production, in AppDelegate.cs I register with the production notification hub such as this:

    var client = newMobileServiceClient(AppConstants.AZURE_PRODUCTION_WEB_API_URL);

  5. Of course i modify the Entitlements.plist aps-environment key to either development or production according to the case.

This worked well for a while but now I get an error in this line:

        try
        {
            // exception is triggered here:
            await client.GetPush().RegisterAsync(deviceToken, templates);                   
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);           
        }

And the error is this:

    2018-12-17 12:03:19.682 MyApp.iOS[7321:2007760] <!DOCTYPE html>
    <html>
        <head>
            <title>The resource cannot be found.</title>
            <meta name="viewport" content="width=device-width" />
            <style>
             body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;} 
             p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}
             b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}
             H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }
             H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }
             pre {font-family:"Consolas","Lucida Console",Monospace;font-size:11pt;margin:0;padding:0.5em;line-height:14pt}
             .marker {font-weight: bold; color: black;text-decoration: none;}
             .version {color: gray;}
             .error {margin-bottom: 10px;}
             .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }

     @media screen and (max-width: 639px) {
              pre { width: 440px; overflow: auto; white-space: pre-wrap; word-wrap: break-word; }
             }
             @media screen and (max-width: 479px) {
              pre { width: 280px; }
             }
            </style>
        </head>

        <body bgcolor="white">

                <span><H1>Server Error in '/' Application.<hr width=100% size=1 color=silver></H1>

                <h2> <i>The resource cannot be found.</i> </h2></span>

                <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">

                <b> Description: </b>HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. &nbsp;Please review the following URL and make sure that it is spelled correctly.
                <br><br>

                <b> Requested URL: </b>/push/installations/53de1cd2-8ba5-403e-950a-f4a1631a5fae<br><br>

                <hr width=100% size=1 color=silver>

                <b>Version 
    Information:</b>&nbsp;Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.7.3163.0

                </font>

        </body>
    </html>
    <!-- 
    [HttpException]: The controller for path &#39;/push/installations/53de1cd2-8ba5-403e-950a-f4a1631a5fae&#39; was not found or does not implement IController.
       at System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType)
       at System.Web.Mvc.DefaultControllerFactory.CreateController(RequestContext requestContext, String controllerName)
       at System.Web.Mvc.MvcHandler.ProcessRequestInit(HttpContextBase httpContext, IController& controller, IControllerFactory& factory)
       at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
       at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, Object state)
       at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncC
    allback cb, Object extraData)
       at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
       at System.Web.HttpApplication.<>c__DisplayClass285_0.<ExecuteStepImpl>b__0()
       at System.Web.HttpApplication.StepInvoker.Invoke(Action executionStep)
       at System.Web.HttpApplication.StepInvoker.<>c__DisplayClass4_0.<Invoke>b__0()
       at Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule.OnExecuteRequestStep(HttpContextBase context, Action step)
       at System.Web.HttpApplication.<>c__DisplayClass284_0.<OnExecuteRequestStep>b__0(Action nextStepAction)
       at System.Web.HttpApplication.StepInvoker.Invoke(Action executionStep)
       at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
       at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
    --><!-- 
    This error page might contain sensitive information because ASP.NET is configured to show verbose error messages using &lt;customErrors mode="Off"
    /&gt;. Consider using &lt;customErrors mode="On"/&gt; or &lt;customErrors mode="RemoteOnly"/&gt; in production environments.-->
    Thread finished: <Thread Pool> #13
    The thread 0xd has exited with code 0 (0x0).

Here is the code in my AppDelegate.cs:

    public override bool FinishedLaunching(UIApplication app, NSDictionary options)
            {

                // This MobileServiceClient has been configured to communicate with the Azure Mobile App and
                // Azure Gateway using the application url. You're all set to start working with your Mobile App!
                //Microsoft.WindowsAzure.MobileServices.MobileServiceClient MyAppClient = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient(
                //"https://myapp.azurewebsites.net");

                global::Xamarin.Forms.Forms.Init();

                //add azure support
                CurrentPlatform.Init();

                //add for using local database
                string dbName = "shakealarm_db.sqlite";
                string dbFolderPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "..", "Library");
                string dbFullPath = Path.Combine(dbFolderPath, dbName);

                //load application with overloaded constructor for local db path --> add dbFullPath as param to new App()
                LoadApplication(new App(dbFullPath, null));

                // first request permission to use push notifications
                RequestPushPermissionAsync();

                // going to store the contents of the notification in the options param
                // extract the content when app is active
                _launchOptions = options;

                return base.FinishedLaunching(app, options);
            }

            public override void OnActivated(UIApplication uiApplication)
            {
                base.OnActivated(uiApplication);

                //If app was not running and we come from a notification badge, the notification is
                if (_launchOptions != null && _launchOptions.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey))
                {
                    var notification = _launchOptions[UIApplication.LaunchOptionsRemoteNotificationKey] as NSDictionary;
                    PresentNotification(notification);
                }
                _launchOptions = null;            
            }

            private async Task RequestPushPermissionAsync()
            {
                // Register for any type of notification (local or remote)
                var requestResult = await UNUserNotificationCenter.Current.RequestAuthorizationAsync(
                    UNAuthorizationOptions.Alert
                    | UNAuthorizationOptions.Badge
                    | UNAuthorizationOptions.Sound);

                // request result produces two items, item1 and item2
                // Item1 = approved boolean
                bool approved = requestResult.Item1;
                NSError error = requestResult.Item2;

                if (error == null)
                {
                    //Handle approval
                    if (!approved)
                    {
                        Console.Write("Permission to receive notifications was not granted.");
                        return;
                    }

                    // check if settings have been modified since last run to disallow permission for push notif.
                    var currentSettings = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
                    if (currentSettings.AuthorizationStatus != UNAuthorizationStatus.Authorized)
                    {
                        Console.WriteLine("Permissions were requested in the past but have been revoked(from settings)");
                        return;
                    }

                    // finally register for push notifications
                    // starts an asycrhonous process, receives a token
                    UIApplication.SharedApplication.RegisterForRemoteNotifications();
                }
                else
                {
                    Console.Write($"Error requesting permissions: {error}.");
                }

            }

            public async override void RegisteredForRemoteNotifications(
                UIApplication application, NSData deviceToken)
            {
                if (deviceToken == null)
                {
                    // can happen in rare conditions 
                    return;
                }

                Console.WriteLine($"Token received: {deviceToken}");

                await SendRegistrationToServerAsync(deviceToken);
            }


            private async Task SendRegistrationToServerAsync(NSData deviceToken)
            {
                //this is the template/payload used by iOS. It contains the "messageParam"
                // that will be replaced by our service

                const string templateBodyAPNS = @"{
                                                    ""aps"" : {
                                                        ""alert"" : ""$(messageParam)"",
                                                        ""mutable-content"": 1
                                                    },
                                                }";


                var templates = new JObject();
                templates["genericMessage"] = new JObject
                {
                    {"body", templateBodyAPNS }
                };

                // send registration to web api for PRODUCTION
                // var client = new MobileServiceClient(MyApp.App.MobileServiceUrl);

                // send registration to web api for DEVELOPMENT
                var client = new MobileServiceClient(AppConstants.AZURE_DEVELOPMENT_WEB_API_URL);       

                try
                {         
                    // exception triggered here
                    await client.GetPush().RegisterAsync(deviceToken, templates);                   
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);           
                }


                //get the installation id for Development
                Console.WriteLine("Installation id1: " + client.GetPush().InstallationId.ToString());
            }

So I am almost sure there is a problem with the installation id triggered by the fact that I have downloaded the app from store and then used the same iPhone in development and there is an installation id/registration mix up for push notifications, but what to do next? How can I reset the installation id?

Any suggestion is welcome please.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    Does this issue always occur when you changed the development environemnt?
    I haven't tested this situation on the same device. But each time you want to change the environment, did you uninstall the previous project?

  • Cdn_EuroCdn_Euro Member ✭✭✭
    edited December 2018

    @LandLu I do not know if every time. It happened when I switched from production back to devleopment the last time.
    I uninstalled the app, actually I erased the app from the device and then deployed it again. Is that what you meant by uninstalling the previous project? I modify the same project in Visual Studio for production/development.

  • Cdn_EuroCdn_Euro Member ✭✭✭
    edited December 2018

    @LandLu Update: I ended using another iPhone device and works perfect. My own iPhone will not work anymore in development but works fine if I download the app from the store(production). There is a problem with the installation and association with the production/development mode, I am not sure how to fix it.

Sign In or Register to comment.