Xamarin.Firebase.iOS.CloudMessaging fails on Messaging.SharedInstance.Delegate - xamarin.ios

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

Related

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

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.

Xamarin.iOS Can I use PlacePicker in my iOS application

how can I implement PlacePicker in my project as I can this doable in iOS native by implement this code
#IBAction func pickPlace(_ sender: UIButton) {
let config = GMSPlacePickerConfig(viewport: nil)
let placePicker = GMSPlacePicker(config: config)
placePicker.pickPlace(callback: { (place, error) -> Void in
if let error = error {
print("Pick Place error: \(error.localizedDescription)")
return
}
guard let place = place else {
print("No place selected")
return
}
print("Place name \(place.name)")
print("Place address \(place.formattedAddress)")
print("Place attributions \(place.attributions)")
})
}
how can I do the same in Xamarin.iOS
First I need nuget to include in my project
Second how can I implement the same code
First follow instruction to get google api key
Second Call this method in FinishedLaunching
string googleApiKey = YOUR_KEY_HERE;
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
ConfigureGoogleMaps();
}
void ConfigureGoogleMaps()
{
PlacesClient.ProvideApiKey(googleApiKey);
MapServices.ProvideAPIKey(googleApiKey);
}
Third add this keys in your Info.plist
<key>LSApplicationQueriesSchemes</key>
<array>
<string>googlechromes</string>
<string>comgooglemaps</string>
</array>
Fourth ask user for permission to use his location Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This application requires location services to work</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>This application requires location services to work</string>
Fifth now we call PlacePicker
var config = new PlacePickerConfig(null);
var placePicker = new PlacePicker(config);
placePicker.PickPlaceWithCallback((result, error) => {
if(error != null){
return;
}
if(result != null){
SelectedPlace = result;
street.Text = result.FormattedAddress;
}
});
If you want know what equivalent to any method name in native looking here

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();
}
}

MediaCapture.CapturePhotoToStreamAsync() and MediaCapture.CapturePhotoToStorageFileAsync() throw Argument exception

I'm trying to create an app that can use the camera for Windows Phone 8.1, using the Windows RT/XAML development model.
When I try to call either of the capture methods off of the MediaCapture class I get an ArgumentException with the message "The parameter is incorrect." Here is my code
private async Task Initialize()
{
if (!DesignMode.DesignModeEnabled)
{
await _mediaCaptureMgr.InitializeAsync();
ViewFinder.Source = _mediaCaptureMgr;
await _mediaCaptureMgr.StartPreviewAsync();
}
}
private async void ViewFinder_OnTapped(object sender, TappedRoutedEventArgs e)
{
ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg();
var stream = new InMemoryRandomAccessStream();
await _mediaCaptureMgr.CapturePhotoToStreamAsync(imageProperties, stream);
_bitmap = new WriteableBitmap((int) ViewFinder.ActualWidth, (int) ViewFinder.ActualHeight);
stream.Seek(0);
await _bitmap.SetSourceAsync(stream);
PreviewImage.Source = _bitmap;
PreviewElements.Visibility = Visibility.Visible;
ViewFinder.Visibility = Visibility.Collapsed;
Buttons.Visibility = Visibility.Visible;
Message.Visibility = Visibility.Collapsed;
stream.Seek(0);
var buffer = new global::Windows.Storage.Streams.Buffer((uint) stream.Size);
stream.ReadAsync(buffer, (uint) stream.Size, InputStreamOptions.None);
DataContext = buffer.ToArray();
if (PhotoCaptured != null)
PhotoCaptured(this, null);
}
The initialize method is called on page load, and the viewfinder_ontapped is called when they tap the CaptureElement I have in the xaml. The error is thrown on
await _mediaCaptureMgr.CapturePhotoToStreamAsync(imageProperties, stream);
What's really bizarre is that I downloaded the latest source for the winrt xaml toolkit http://winrtxamltoolkit.codeplex.com/ and tried their sample camera app, which uses similar code. It throws the same error on MediaCapture.CapturePhotoToStorageFileAsync(). Can anyone help me identify why?

RFCommConnectionTrigger in Windows Universal Apps To detect Incoming Bluetooth Connection

I am working on a Windows Universal App. I Want to get the Data from a Bluetooth Device to the Windows Phone. I am Using the Concept of RFCommCommunicationTrigger for this Purpose.
Here's the code Snippet I am Using
var rfTrigger = new RfcommConnectionTrigger();
// Specify what the service ID is
rfTrigger.InboundConnection.LocalServiceId = RfcommServiceId.FromUuid(new Guid("<some_base_guid>"));
//Register RFComm trigger
var rfReg = RegisterTaskOnce(
"HWRFCommTrigger",
"BackgroundLibrary.RFBackgroundTask",
rfTrigger, null
);
SetCompletedOnce(rfReg, OnTaskCompleted);
Here the Function of RegisterTaskOnce
static private IBackgroundTaskRegistration RegisterTaskOnce(string taskName, string entryPoint, IBackgroundTrigger trigger, params IBackgroundCondition[] conditions)
{
// Validate
if (string.IsNullOrEmpty(taskName)) throw new ArgumentException("taskName");
if (string.IsNullOrEmpty(entryPoint)) throw new ArgumentException("entryPoint");
if (trigger == null) throw new ArgumentNullException("trigger");
// Look to see if the name is already registered
var existingReg = (from reg in BackgroundTaskRegistration.AllTasks
where reg.Value.Name == taskName
select reg.Value).FirstOrDefault();
Debug.WriteLine("Background task "+ taskName+" is already running in the Background");
// If already registered, just return the existing registration
if (existingReg != null)
{
return existingReg;
}
// Create the builder
var builder = new BackgroundTaskBuilder();
builder.TaskEntryPoint = entryPoint;
builder.Name = taskName;
builder.SetTrigger(trigger);
// Conditions?
if (conditions != null)
{
foreach (var condition in conditions)
{
builder.AddCondition(condition);
}
}
// Register
return builder.Register();
}
Here's the code for SetCompletedOnce this will add a Handler only once
static private void SetCompletedOnce(IBackgroundTaskRegistration reg, BackgroundTaskCompletedEventHandler handler)
{
// Validate
if (reg == null) throw new ArgumentNullException("reg");
if (handler == null) throw new ArgumentNullException("handler");
// Unsubscribe in case already subscribed
reg.Completed -= handler;
// Subscribe
reg.Completed += handler;
}
I have also Written the BackgroundLibrary.RFBackgroundTask.cs
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
Debug.WriteLine(taskInstance.TriggerDetails.GetType());
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
Debug.WriteLine("RFComm Task Running");
Debug.WriteLine(taskInstance.TriggerDetails.GetType().ToString());
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}
The Run Method is Invoked Every Time The Device tries to Open the Connection.
The type of the Trigger that is obtained (the type I am debugging in the run method of the RFBackgroundTask.cs) is printed as
Windows.Devices.Bluetooth.Background.RfcommConnectionTriggerDetails
But I am Unable use that because I dont have this Class in the BackgroundLibrary project.
The Documentation says that this Provides information about the Bluetooth device that caused this trigger to fire.
It has Variables like Socket,RemoteDevice etc.
I think I am Missing something very simple
Can you please help me out .
Once your background task is launched, simply cast the TriggerDetails object to an RfcommConnectionTriggerDetails object:
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
RfcommConnectionTriggerDetails details = (RfcommConnectionTriggerDetails)taskInstance.TriggerDetails;
StreamSocket = details.Socket; // Rfcomm Socket
// Access other properties...
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}

Resources