.Net Maui iOS app with Silent Push Notifications ReceivedRemoteNotification or DidReceiveRemoteNotification never gets called - xamarin.ios

I'm using Plugin.Firebase and my own APN Server. I can send a message with an alert payload. This works just fine. If I send a message with a content-available payload, nothing happens.
I'm using the current version of the .Net 7 release. My target is iOS 16. My test device is an iPhone 12 running iOS 15.7.
Here are some key components of my AppDelegate.
This has been added to FinishedLaunching.
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
//this.Log($"RequestAuthorization: {granted}" + (error != null ? $" with error: {error.LocalizedDescription}" : string.Empty));
if (granted && error == null)
{
this.InvokeOnMainThread(() =>
{
UIApplication.SharedApplication.RegisterForRemoteNotifications();
//this.InitFirebase();
UNUserNotificationCenter.Current.Delegate = this;
});
}
});
Here is DidReceiveRemoteNotification.
https://learn.microsoft.com/en-us/dotnet/api/uikit.uiapplicationdelegate.didreceiveremotenotification?view=xamarin-ios-sdk-12
//App is in the foreground
[Foundation.Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")]
public void DidReceiveRemoteNotification(UIKit.UIApplication application, Foundation.NSDictionary userInfo, Action<UIKit.UIBackgroundFetchResult> completionHandler)
{
SentrySdk.CaptureMessage("DidReceiveNotificationResponse = " + "yes");
ProcessNotification(userInfo, false);
completionHandler(UIBackgroundFetchResult.NewData);
}
Here is ReceivedRemoteNotification.
https://learn.microsoft.com/en-us/dotnet/api/uikit.uiapplicationdelegate.receivedremotenotification?view=xamarin-ios-sdk-12
//App is in the background
[Foundation.Export("application:didReceiveRemoteNotification:")]
public void ReceivedRemoteNotification(UIKit.UIApplication application, Foundation.NSDictionary userInfo)
{
SentrySdk.CaptureMessage("ReceivedRemoteNotification = " + "yes");
ProcessNotification(userInfo, false);
}
Here is the APS payload.
public AppleNotificationSilent()
{
//Id = id;
Aps = new ApsPayload
{
//AlertBody = new ApsPayload.Alert
//{
// Title = title,
// SubTitle = subtitle,
// Body = message
//},
//Alert = "",
ContentAvailable = 1,
//PushType = "background",
//Priority = 5,
CustomKey = "GetData" //THIS WORKS
};
//CustomKey = "GetData";
}
Here is what ContentAvailable looks like.
[JsonProperty("content-available")]
public int ContentAvailable { get; set; }
Finally, I've changed the following headers for silent push.
apns-push-type = "background"
apns-priority = 5
I've noticed that the overrides for DidReceiveRemoteNotification and ReceivedRemoteNotification aren't found. This was supposed to added to the .Net 7 Maui release. I've also noticed that the decoration Export for both methods is the same except for fetchCompletionHandler in DidReceiveRemoteNotification.
Here is some additional information on how this is supposed to work.
https://learn.microsoft.com/en-us/xamarin/ios/app-fundamentals/backgrounding/ios-backgrounding-techniques/updating-an-application-in-the-background
https://learn.microsoft.com/en-us/xamarin/ios/platform/user-notifications/enhanced-user-notifications?source=recommendations&tabs=macos
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app
Does anyone know why this isn't working? Thanks!
BEGIN UPDATE 1
A silent push notification will call this method when the app is open and in the foreground.
// App is in the foreground
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
SentrySdk.CaptureMessage("DidReceiveNotificationResponse = " + "yes");
ShowLocalNotification();
//this.Log($"{nameof(DidReceiveNotificationResponse)}: " + response.Notification.Request.Content.UserInfo);
completionHandler();
}
END UPDATE 1
BEGIN UPDATE 2
After adding the fix found in the answer, all silent push notifications are now handled by DidReceiveRemoteNotification.
END UPDATE 2

Make sure you have this in your csprog file. I'm now able to receive silent push notifications when the app is open / being used, open / in the background, and while not running at all.
https://github.com/dotnet/maui/issues/6259#issuecomment-1109359755
Here is what mine looks like.
<PropertyGroup>
<MtouchExtraArgs>--optimize:-static-block-to-delegate-lookup</MtouchExtraArgs>
<CodesignKey>iPhone Developer</CodesignKey>
</PropertyGroup>
The weird part about this is that all the silent push messages are being handled by DidReceiveRemoteNotification.

Related

Xamarin.Firebase.iOS.CloudMessaging fails on Messaging.SharedInstance.Delegate

I have tried many different versions of sample / example code, but I cannot get Cloud Messaging to work on iOS (14.5).
The line
Messaging.SharedInstance.Delegate = this;
Always fails with SharedInstance being null.
To test, I created a new Xamarin Forms project, I added the Nuget package Xamarin.Firebase.iOS.CloudMessaging version 4.7.1, and I added the GoogleServices-Info.plist file and set the Build Action to BundleResource.
I added the following code to AppDelegate:
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate, IMessagingDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
try
{
global::Xamarin.Forms.Forms.Init();
Firebase.Core.App.Configure();
LoadApplication(new App());
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) => {
Console.WriteLine(granted);
});
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
// For iOS 10 data message (sent via FCM)
Messaging.SharedInstance.Delegate = this;
}
else
{
// iOS 9 or before
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
return true;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
The above code fails on the line "Messaging.SharedInstance.Delegate = this;".
This issue is also on the GitHub issues section:
https://github.com/xamarin/GoogleApisForiOSComponents/issues/486

Azure cognitive Service - Speech sample code failed with authentication error

The program returns: CANCELED: Reason=Error ErrorDetails=WebSocket Upgrade failed with an authentication error (401). Please check for correct subscription key (or authorization token) and region name. SessionId: cbfcdf7f26304343a08de6c398652053
I'm using my free trial subscription key and westus region. This is the sample code found here: https://learn.microsoft.com/en-us/azure/cognitive-services/speech-service/quickstarts/speech-to-text-from-microphone?tabs=unity%2Cx-android%2Clinux%2Cjava-runtime&pivots=programming-language-csharp
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;
#if PLATFORM_ANDROID
using UnityEngine.Android;
#endif
#if PLATFORM_IOS
using UnityEngine.iOS;
using System.Collections;
#endif
public class Helloworld : MonoBehaviour
{
// Hook up the two properties below with a Text and Button object in your UI.
public Text outputText;
public Button startRecoButton;
private object threadLocker = new object();
private bool waitingForReco;
private string message;
private bool micPermissionGranted = false;
#if PLATFORM_ANDROID || PLATFORM_IOS
// Required to manifest microphone permission, cf.
// https://docs.unity3d.com/Manual/android-manifest.html
private Microphone mic;
#endif
public async void ButtonClick()
{
// Creates an instance of a speech config with specified subscription key and service region.
// Replace with your own subscription key and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("yourSubscriptionKey", "yourRegion");
// Make sure to dispose the recognizer after use!
using (var recognizer = new SpeechRecognizer(config))
{
lock (threadLocker)
{
waitingForReco = true;
}
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
var result = await recognizer.RecognizeOnceAsync().ConfigureAwait(false);
// Checks result.
string newMessage = string.Empty;
if (result.Reason == ResultReason.RecognizedSpeech)
{
newMessage = result.Text;
}
else if (result.Reason == ResultReason.NoMatch)
{
newMessage = "NOMATCH: Speech could not be recognized.";
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = CancellationDetails.FromResult(result);
newMessage = $"CANCELED: Reason={cancellation.Reason} ErrorDetails={cancellation.ErrorDetails}";
}
lock (threadLocker)
{
message = newMessage;
waitingForReco = false;
}
}
}
void Start()
{
if (outputText == null)
{
UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
}
else if (startRecoButton == null)
{
message = "startRecoButton property is null! Assign a UI Button to it.";
UnityEngine.Debug.LogError(message);
}
else
{
// Continue with normal initialization, Text and Button objects are present.
#if PLATFORM_ANDROID
// Request to use the microphone, cf.
// https://docs.unity3d.com/Manual/android-RequestingPermissions.html
message = "Waiting for mic permission";
if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Permission.RequestUserPermission(Permission.Microphone);
}
#elif PLATFORM_IOS
if (!Application.HasUserAuthorization(UserAuthorization.Microphone))
{
Application.RequestUserAuthorization(UserAuthorization.Microphone);
}
#else
micPermissionGranted = true;
message = "Click button to recognize speech";
#endif
startRecoButton.onClick.AddListener(ButtonClick);
}
}
void Update()
{
#if PLATFORM_ANDROID
if (!micPermissionGranted && Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#elif PLATFORM_IOS
if (!micPermissionGranted && Application.HasUserAuthorization(UserAuthorization.Microphone))
{
micPermissionGranted = true;
message = "Click button to recognize speech";
}
#endif
lock (threadLocker)
{
if (startRecoButton != null)
{
startRecoButton.interactable = !waitingForReco && micPermissionGranted;
}
if (outputText != null)
{
outputText.text = message;
}
}
}
}
The sample code you pasted above still has the placeholder values for region and subscription key. Just double checking that you did in fact replace those strings with your own subscription key and region? If that's true, can you please turn on logging, run the code again, and then provide the log? We can help diagnose from there...
To turn on logging, see https://aka.ms/speech/logging.

ZXing.Net.Mobile Sample.WindowsUniversal Sample Not Scanning

Testing this to incorporate into Win 10 UWP app to scan 1D barcodes (format 39 & 128). I have updated latest through nuget 2.0.4.46. Referenced post at http://www.yortondotnet.com/2015/07/mobile-barcode-scanning-with-zxingnet.html regarding some options setting prior to scan() with no luck. The scanner (camera) opens but never recognizes a barcode scan successfully - or failure for that matter. It seems nothing is happening whatsoever. I have included straight, pertinent sample code with some options modifications for review. I have gotten Scandit API to work and was going to try Manateeworks but both are really costly and not an option. I am developing on Surface Pro 3 (Win 10) and that build will also be target machines when complete.
public sealed partial class MainPage : Page
{
UIElement customOverlayElement = null;
MobileBarcodeScanner scanner;
public MainPage()
{
this.InitializeComponent();
//Create a new instance of our scanner
scanner = new MobileBarcodeScanner(this.Dispatcher);
scanner.Dispatcher = this.Dispatcher;
}
private void buttonScanDefault_Click(object sender, RoutedEventArgs e)
{
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of our default overlay
scanner.TopText = "Hold camera up to barcode";
scanner.BottomText = "Camera will automatically scan barcode\r\n\r\nPress the 'Back' button to Cancel";
// GWS Set Options
var options = new MobileBarcodeScanningOptions();
options.PossibleFormats = new List<ZXing.BarcodeFormat>() {
ZXing.BarcodeFormat.CODE_39, ZXing.BarcodeFormat.CODE_128
};
options.AutoRotate = false;
options.TryHarder = false;
options.TryInverted = false;
//Start scanning
scanner.Scan(options).ContinueWith(t =>
{
if (t.Result != null)
HandleScanResult(t.Result);
});
}
private void buttonScanContinuously_Click(object sender, RoutedEventArgs e)
{
//Tell our scanner to use the default overlay
scanner.UseCustomOverlay = false;
//We can customize the top and bottom text of our default overlay
scanner.TopText = "Hold camera up to barcode";
scanner.BottomText = "Camera will automatically scan barcode\r\n\r\nPress the 'Back' button to Cancel";
// GWS Set Options
var options = new MobileBarcodeScanningOptions();
options.PossibleFormats = new List<ZXing.BarcodeFormat>() {
ZXing.BarcodeFormat.CODE_39, ZXing.BarcodeFormat.CODE_128
};
options.AutoRotate = false;
options.TryHarder = false;
options.TryInverted = false;
//Start scanning
scanner.ScanContinuously(options, async (result) =>
{
var msg = "Found Barcode: " + result.Text;
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
await MessageBox(msg);
});
});
}
async void HandleScanResult(ZXing.Result result)
{
string msg = "";
if (result != null && !string.IsNullOrEmpty(result.Text))
msg = "Found Barcode: " + result.Text;
else
msg = "Scanning Canceled!";
await MessageBox(msg);
}
}
Simon,
I have the exact same problem. I tested your code with the latest nuget 2.1.47, the problem still exists.
You need to download the latest from Github and add the following projects (or DLLs) to your project:
ZXing.Net (project: zxing.portable.csproj)
ZXing.Net.Mobile.Core
ZXing.Net.Mobile.WindowsUniversal
I have tested your code and it works fine. I hope this help.
Cheers,
Sam
I think that the problem in the hardware you are testing with. Surface Pro 3 (Win 10) does not have an auto focus camera. I've never succeed to scan with ZXing using my Surface Pro 3, while the same application is working fine with my other windows 10 device.

Back arrow does not work when UWP launched from WinForms app

So we are integrating the old with the new.
I am launching my UWP app from within our WinForms app.
When I navigate around the UWP app, the back button does not work. When launching the UWP app normally everything works fine.
Here is my winforms launching code:
Uri uri = new Uri($"companies:");
//see declarations in package.appxmanifest in winten app.
string targetPackageFamilyName = "81e1fc62-68df-45f5-ac35-c86d1277e2db_2zt4j53vqbz02";
// see added protocol declaration in package.appxmanifest in win10 app
var supportStatus = await Launcher.QueryUriSupportAsync(
uri,
LaunchQuerySupportType.Uri,
targetPackageFamilyName);
if (supportStatus != LaunchQuerySupportStatus.Available)
{
var msg = "Can't launch because the app we need is " + supportStatus.ToString();
}
else
{
var options = new LauncherOptions { TargetApplicationPackageFamilyName = targetPackageFamilyName };
var success = await Launcher.LaunchUriAsync(uri, options);
}
And here is the receiving code
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
if (args.Kind == ActivationKind.Protocol)
{
ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs;
switch (eventArgs.Uri.Scheme)
{
case "companies":
NavigationService.Navigate(typeof(Views.CompaniesPage));
break;
case "company":
NavigationService.Navigate(typeof(Views.CompanyEditPage), eventArgs.Uri.Query);
break;
case "query":
NavigationService.Navigate(typeof(Views.QueryPage));
break;
default:
break;
}
}
else
{
NavigationService.Navigate(typeof(Views.CompaniesPage));
await Task.Yield();
}
}

Couldn't send push notification using Azure Notification Hubs

I am trying to create a notification bus to send push notifications to my windows phone App.
I referred the link and followed all the steps.
My application is registering httpchannel
var channel = HttpNotificationChannel.Find("MyPushChannel");
if (channel == null)
{
channel = new HttpNotificationChannel("MyPushChannel");
channel.Open();
channel.BindToShellToast();
}
channel.ChannelUriUpdated += async (o, args) =>
{
var hub = new NotificationHub("xyz", "Endpoint=sb://xyz-ns.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=DWNOTQgqiKsDsTpXx4kUyeBPLsdSlFEP2YZIMW1dMEU=");
await hub.RegisterNativeAsync(args.ChannelUri.ToString());
};
And the console application
private static async void SendNotificationAsync()
{
var hub = NotificationHubClient
.CreateClientFromConnectionString("Endpoint=sb://xyz-ns.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=6P1isg5Uv97hJxRqbvJ11yJL+UV49RTSZNALJAoVCzI=",
"xyz");
const string toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>Hello from a .NET App!</wp:Text1>" +
"</wp:Toast> " +
"</wp:Notification>";
await hub.SendMpnsNativeNotificationAsync(toast);
}
I have even enabled unauthorized notifications in the portal. However when I run the console application, I am unable to get the notification.
And My console application looks like this
static void Main()
{
SendNotificationAsync();
}
Can somebody help me what I need to do to fix this?

Resources