Open punio opened 1 year ago
I got the same problem. @punio did you find a solution?
@FM1973 I found only out that can't use it as it is with MAUI. It may be possible with Shiny, but I haven't tried it yet.
@punio I took another road. I´m now using Azure NotificationHub only on the server side. On the client side I did the implementation like described in the following link: https://learn.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms#configure-the-native-android-project-for-push-notifications
Although I changed some bits and replaced deprecated code with the code one should use now.
Works pretty well...
@FM1973 Thank you for letting me know how to solve it. I will try that solution too.
@punio I took another road. I´m now using Azure NotificationHub only on the server side. On the client side I did the implementation like described in the following link: https://learn.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms#configure-the-native-android-project-for-push-notifications
Although I changed some bits and replaced deprecated code with the code one should use now.
Works pretty well...
Hi, do you get the IOS working or android version? If is IOS, can you share how you achieve it?
According to this https://github.com/xamarin/XamarinComponents/issues/1418 Xamarin.Azure.NotificationHubs.iOS is not going to be further supported! So currently, what @FM1973 suggested seems the only solution, not only for MAUI but for .Net iOS in .Net 6 also. Any input is more than welcome!
I solved iOS push by using Microsoft.Azure.NotificationHubs
var hub = NotificationHubClient.CreateClientFromConnectionString(this.connectionString, this.notificationHubName);
var installation = new Installation
{
InstallationId = deviceToken,
PushChannel = deviceToken,
Platform = NotificationPlatform.Apns,
Tags = Array.Empty
await hub.CreateOrUpdateInstallationAsync(installation);
deviceToken is obtained from RegisteredForRemoteNotifications
Yeah, I've just finished migrating my app to Microsoft.Azure.NotificationHubs as well. I just need to get the token from APNS (or Firebase on Android) and feed that to the Installation request.
@rimex-bkimmett Please see this thread https://github.com/Azure/azure-notificationhubs-dotnet/issues/298. I have created a boiler plate repo (https://github.com/RobertHedgate/MauiAppPush) on how I got it to work but more questions are answered is that thread,
Now it's explained in the docs: https://learn.microsoft.com/en-us/dotnet/maui/data-cloud/push-notifications?view=net-maui-8.0#create-a-net-maui-app
They show how to do it with api key instead of ListenConnectionString which has some benefits but of course adds some complexity. @RobertHedgate proposal still looks like the one with the least amount of boilerplate code to make everything work.
I believe the only "issue" with @RobertHedgate proposal is that it doensn't refresh the azure hub installation when the token changes in the onNewToken
function.
In my case, I did a hybrid between both. I followed the tutorial, but my NotificationRegistrationService
(the one that they use the api key and the http calls) is like this:
public class NotificationRegistrationService : INotificationRegistrationService
{
const string CachedDeviceTokenKey = "cached_notification_hub_device_token";
const string CachedTagsKey = "cached_notification_hub_tags";
const string CachedUserIdKey = "cached_notification_hub_user_id";
IDeviceInstallationService _deviceInstallationService;
private NotificationHubClient _hub;
public NotificationRegistrationService(IDeviceInstallationService deviceInstallationService)
{
_deviceInstallationService = deviceInstallationService ?? throw new ArgumentNullException(nameof(deviceInstallationService));
_hub = NotificationHubClient.CreateClientFromConnectionString(Config.ListenConnectionString, Config.NotificationHubName);
}
public async Task DeregisterDeviceAsync()
{
var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
.ConfigureAwait(false);
if (cachedToken == null)
return;
var deviceId = GetDeviceId();
await _hub.DeleteInstallationAsync(deviceId);
SecureStorage.Remove(CachedDeviceTokenKey);
SecureStorage.Remove(CachedTagsKey);
SecureStorage.Remove(CachedUserIdKey);
}
public async Task RegisterDeviceAsync(string userId, params string[] tags)
{
var deviceInstallation = _deviceInstallationService?.GetDeviceInstallation(tags);
if (!string.IsNullOrEmpty(userId))
deviceInstallation.UserId = userId;
await _hub.CreateOrUpdateInstallationAsync(deviceInstallation);
await SecureStorage.SetAsync(CachedDeviceTokenKey, deviceInstallation.PushChannel)
.ConfigureAwait(false);
await SecureStorage.SetAsync(CachedTagsKey, JsonSerializer.Serialize(tags));
await SecureStorage.SetAsync(CachedUserIdKey, userId);
}
public async Task RefreshRegistrationAsync()
{
var cachedToken = await SecureStorage.GetAsync(CachedDeviceTokenKey)
.ConfigureAwait(false);
var serializedTags = await SecureStorage.GetAsync(CachedTagsKey)
.ConfigureAwait(false);
var cachedUserId = await SecureStorage.GetAsync(CachedUserIdKey)
.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(cachedToken) ||
string.IsNullOrWhiteSpace(serializedTags) ||
string.IsNullOrEmpty(cachedUserId) ||
string.IsNullOrWhiteSpace(_deviceInstallationService.Token) ||
cachedToken == _deviceInstallationService.Token)
return;
var tags = JsonSerializer.Deserialize<string[]>(serializedTags);
await RegisterDeviceAsync(cachedUserId, tags);
}
private string _userIdPropName;
private string UserIdPropName => _userIdPropName ??= "/" + GetJsonPropertyName(typeof(Installation), nameof(Installation.UserId));
private string _tagsPropName;
private string TagsPropName => _tagsPropName ??= GetJsonPropertyName(typeof(Installation), nameof(Installation.Tags));
public async Task UpdateUserId(string userName)
{
var deviceId = GetDeviceId();
var updates = new List<PartialUpdateOperation>
{
new() { Operation = UpdateOperationType.Replace, Path = UserIdPropName, Value = userName },
await GetUpdateTagOperation(new[] { (NotificationTags.Username, userName) })
};
await _hub.PatchInstallationAsync(deviceId, updates);
}
public async Task UpdateTag(NotificationTags tag, string value)
{
var deviceId = GetDeviceId();
var updates = new List<PartialUpdateOperation>
{
await GetUpdateTagOperation(new[] { (tag, value) })
};
await _hub.PatchInstallationAsync(deviceId, updates);
}
private async Task<PartialUpdateOperation> GetUpdateTagOperation(IEnumerable<(NotificationTags, string)> newTags)
{
var serializedTags = await SecureStorage.GetAsync(CachedTagsKey)
.ConfigureAwait(false);
var tags = new List<string>();
if (!string.IsNullOrWhiteSpace(serializedTags))
tags.AddRange(JsonSerializer.Deserialize<string[]>(serializedTags));
foreach (var (tagType, value) in newTags)
{
var tagId = NotificationHubUtils.GetTagId(tagType);
var tagIndex = tags.FindIndex(a => a.StartsWith(tagId));
var tag = NotificationHubUtils.GetTag(tagType, value);
if (tagIndex >= 0)
tags[tagIndex] = tag;
else
tags.Add(tag);
}
return new() { Operation = UpdateOperationType.Replace, Path = TagsPropName, Value = JsonSerializer.Serialize(tags) };
}
private string GetDeviceId()
{
var deviceId = _deviceInstallationService?.GetDeviceId();
if (string.IsNullOrWhiteSpace(deviceId))
throw new Exception("Unable to resolve an ID for the device.");
return deviceId;
}
private static string GetJsonPropertyName(Type type, string propertyName)
{
if (type is null)
throw new ArgumentNullException(nameof(type));
return type.GetProperty(propertyName)
?.GetCustomAttribute<Newtonsoft.Json.JsonPropertyAttribute>()
?.PropertyName;
}
}
:warning: I think currently there is an issue with their Android implementation in their example. They implement
Android.Gms.Tasks.IOnSuccessListener
on the Main activity, but they don't add it as a listener to anything. My guess is that they wanted to add what in java is like:FirebaseMessaging.getInstance().getToken().addOnCompleteListener(IOnSuccessListener)
. The solution is to simply set theDeviceInstallation.Token
toFirebase.Instance.getToken()
before theRegisterDevice
function is called (Firebase docs recommend updating it in the OnCreate function)
Question I used Xamarin.Azure.NotificationHubs.iOS with Xamarin.Forms, but with MAUI NuGet Package installation shows NU1202 and I can't install.
Why is this not a Bug or a feature Request? I'm not very familiar with MAUI, but it seems that most of the Xamarin libraries can be used with MAUI.
Setup (please complete the following information if applicable):