This is a fairly long piece of code but I am getting nowhere with this and cannot see any issues, although I am new to using notification hubs. I am trying to register for targeted notifications (the logged on user) using the notification hub in Azure. After the registration, a test notification is sent.
The issue I am having is that sometimes the notification is sent to the device, and sometimes it is not. It mostly isn't but occasionally when I step through the code on the server, i will get the notification on the emulator come through. Once when I deployed the app to my phone the notification came though on the emulator! I cannot discover a pattern.
My Controller class looks like this;
private NotificationHelper hub;
public RegisterController()
{
hub = NotificationHelper.Instance;
}
public async Task<RegistrationDescription> Post([FromBody]JObject registrationCall)
{
var obj = await hub.Post(registrationCall);
return obj;
}
And the helper class (which is used elsewhere so is not directly in the controller) looks like this;
public static NotificationHelper Instance = new NotificationHelper();
public NotificationHubClient Hub { get; set; }
// Create the client in the constructor.
public NotificationHelper()
{
var cn = "<my-cn>";
Hub = NotificationHubClient.CreateClientFromConnectionString(cn, "<my-hub>");
}
public async Task<RegistrationDescription> Post([FromBody] JObject registrationCall)
{
// Get the registration info that we need from the request.
var platform = registrationCall["platform"].ToString();
var installationId = registrationCall["instId"].ToString();
var channelUri = registrationCall["channelUri"] != null
? registrationCall["channelUri"].ToString()
: null;
var deviceToken = registrationCall["deviceToken"] != null
? registrationCall["deviceToken"].ToString()
: null;
var userName = HttpContext.Current.User.Identity.Name;
// Get registrations for the current installation ID.
var regsForInstId = await Hub.GetRegistrationsByTagAsync(installationId, 100);
var updated = false;
var firstRegistration = true;
RegistrationDescription registration = null;
// Check for existing registrations.
foreach (var registrationDescription in regsForInstId)
{
if (firstRegistration)
{
// Update the tags.
registrationDescription.Tags = new HashSet<string>() {installationId, userName};
// We need to handle each platform separately.
switch (platform)
{
case "windows":
var winReg = registrationDescription as MpnsRegistrationDescription;
winReg.ChannelUri = new Uri(channelUri);
registration = await Hub.UpdateRegistrationAsync(winReg);
break;
case "ios":
var iosReg = registrationDescription as AppleRegistrationDescription;
iosReg.DeviceToken = deviceToken;
registration = await Hub.UpdateRegistrationAsync(iosReg);
break;
}
updated = true;
firstRegistration = false;
}
else
{
// We shouldn't have any extra registrations; delete if we do.
await Hub.DeleteRegistrationAsync(registrationDescription);
}
}
// Create a new registration.
if (!updated)
{
switch (platform)
{
case "windows":
registration = await Hub.CreateMpnsNativeRegistrationAsync(channelUri,
new string[] {installationId, userName});
break;
case "ios":
registration = await Hub.CreateAppleNativeRegistrationAsync(deviceToken,
new string[] {installationId, userName});
break;
}
}
// Send out a test notification.
await SendNotification(string.Format("Test notification for {0}", userName), userName);
return registration;
And finally, my SendNotification method is here;
internal async Task SendNotification(string notificationText, string tag)
{
try
{
var toast = PrepareToastPayload("<my-hub>", notificationText);
// Send a notification to the logged-in user on both platforms.
await NotificationHelper.Instance.Hub.SendMpnsNativeNotificationAsync(toast, tag);
//await hubClient.SendAppleNativeNotificationAsync(alert, tag);
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
Console.WriteLine(ex.Message);
}
}
I suspect the issue is in my phone client code, which is here and SubscribeToService is called immediately after WebAPI login;
public void SubscribeToService()
{
_channel = HttpNotificationChannel.Find("mychannel");
if (_channel == null)
{
_channel = new HttpNotificationChannel("mychannel");
_channel.Open();
_channel.BindToShellToast();
}
_channel.ChannelUriUpdated += async (o, args) =>
{
var hub = new NotificationHub("<my-hub>", "<my-cn>");
await hub.RegisterNativeAsync(args.ChannelUri.ToString());
await RegisterForMessageNotificationsAsync();
};
}
public async Task RegisterForMessageNotificationsAsync()
{
using (var client = GetNewHttpClient(true))
{
// Get the info that we need to request registration.
var installationId = LocalStorageManager.GetInstallationId(); // a new Guid
var registration = new Dictionary<string, string>()
{
{"platform", "windows"},
{"instId", installationId},
{"channelUri", _channel.ChannelUri.ToString()}
};
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(ApiUrl + "api/Register/RegisterForNotifications"));
request.Content = new StringContent(JsonConvert.SerializeObject(registration), Encoding.UTF8, "application/json");
string message;
try
{
HttpResponseMessage response = await client.SendAsync(request);
message = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
message = ex.Message;
}
_registrationId = message;
}
}
Any help would be greatly appriciated as I have been stuck on this now for days! I know this is a lot of code to paste up here but it is all relevant. Thanks,
EDIT: The SubscribeToService() method is called when the user logs in and authenticates with the WebAPI. The method is here;
public async Task<User> SendSubmitLogonAsync(LogonObject lo)
{
_logonObject = lo;
using (var client = GetNewHttpClient(false))
{
var logonString = String.Format("grant_type=password&username={0}&password={1}", lo.username, lo.password);
var sc = new StringContent(logonString, Encoding.UTF8);
var response = await client.PostAsync("Token", sc);
if (response.IsSuccessStatusCode)
{
_logonResponse = await response.Content.ReadAsAsync<TokenResponseModel>();
var userInfo = await GetUserInfoAsync();
if (_channel == null)
SubscribeToService();
else
await RegisterForMessageNotificationsAsync();
return userInfo;
}
// ...
}
}
I have solved the issue. There are tons of fairly poorly organised howto's for azure notification hubs and only one of them has this note toward the bottom;
NOTE:
You will not receive the notification when you are still in the app. To receive a toast notification while the app is active, you must handle the ShellToastNotificationReceived event.
This is why I was experiencing intermittent results, as i assumed you would still get a notification if you were in the app. And this little note is pretty well hidden.