I am using Monotouch for mac and have gone through the steps to retrieve a provisioning profile certificate enabling push notification in the process. I have a working app and am now experimenting with apns-sharp and moon-apns but cant' figure out how to retrieve my device token. I'm hoping someone can provide me with detailed and straightforward steps to achieve this.
In your FinishedLaunching method, register the app for remote notifications, through the UIApplication object you get in it:
// Pass the UIRemoteNotificationType combinations you want
app.RegisterForRemoteNotificationTypes(UIRemoteNotificationType.Alert |
UIRemoteNotificationType.Sound);
Then, in your AppDelegate class, override the RegisteredForRemoteNotifications method:
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
// The device token
byte[] token = deviceToken.ToArray();
}
You also have to override the FailedToRegisterForRemoteNotifications method, to handle the error, if any:
public override void FailedToRegisterForRemoteNotifications (UIApplication application, NSError error)
{
// Do something with the error
}
As of iOS deviceToken has changed. The following code worked for me to convert deviceToken as NSData to a string.
string deviceTokenString;
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
deviceTokenString = BitConverter.ToString(deviceToken.ToArray()).Replace("-", string.Empty);
}
else
{
deviceTokenString = Regex.Replace(deviceToken.ToString(), "[^0-9a-zA-Z]+", string.Empty);
}
For me this was only half of the resolution. To use the DeviceToken from a webserver (PHP in my case), the DeviceToken needs to be a Hex String used in the PHP code for firing the Push Notification (e.g. as shown here: [Using PHP to send iOS Push Notifications via APNs
However, the NSdata object offers no simple way to provide that Hex String.
So my "RegisteredForRemoteNotifications" success handler is now:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
// Get current device token
var DeviceToken = Tools.ByteToHex(deviceToken.ToArray());
string DeviceID = UIDevice.CurrentDevice.IdentifierForVendor.AsString();
System.Console.WriteLine("### UserNotification Device Token = " + DeviceToken + ", DeviceID = " + DeviceID);
// Get previous device token
var oldDeviceToken = NSUserDefaults.StandardUserDefaults.StringForKey("PushDeviceToken");
// Has the token changed?
if (string.IsNullOrEmpty(oldDeviceToken) || !oldDeviceToken.Equals(DeviceToken))
{
//### todo: Populate POSTdata set
//### todo: Send POSTdata to URL
// Save new device token
NSUserDefaults.StandardUserDefaults.SetString(DeviceToken, "PushDeviceToken");
}
}
And for the Byte to Hex Conversion:
public static string ByteToHex(byte[] data)
{
StringBuilder sb = new StringBuilder(data.Length * 2);
foreach (byte b in data)
{
sb.AppendFormat("{0:x2}", b);
}
return sb.ToString();
}
Now you can use the DeviceToken in PHP to create the PushNotification submission.
Related
I followed this tutorial to implement Push-Notifications in my Xamarin-Forms App (especially the iOS part). Now my problem is, when I press the register-button, I get the error message "Unable to resolve token for APNS".
Stepping through the code in debug mode I could verify, that the Token property in DeviceInstallationService is indeed null.
So I've gone one step back, and identified that the Token is set only via RegisteredForRemoteNotification in AppDelegate.cs, but this method is never called when I run the App.
Here is some code: App-Delegate
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Foundation;
using Notes.iOS.Extensions;
using Notes.iOS.Services;
using Notes.Services;
using UIKit;
using UserNotifications;
using Xamarin.Essentials;
using System.Collections.Generic;
using System.Linq;
using Syncfusion.SfCalendar.XForms.iOS;
namespace Notes.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
IPushDemoNotificationActionService _notificationActionService;
INotificationRegistrationService _notificationRegistrationService;
IDeviceInstallationService _deviceInstallationService;
IPushDemoNotificationActionService NotificationActionService
=> _notificationActionService ??
(_notificationActionService =
ServiceContainer.Resolve<IPushDemoNotificationActionService>());
INotificationRegistrationService NotificationRegistrationService
=> _notificationRegistrationService ??
(_notificationRegistrationService =
ServiceContainer.Resolve<INotificationRegistrationService>());
IDeviceInstallationService DeviceInstallationService
=> _deviceInstallationService ??
(_deviceInstallationService =
ServiceContainer.Resolve<IDeviceInstallationService>());
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Bootstrap.Begin(() => new DeviceInstallationService());
if (DeviceInstallationService.NotificationsSupported)
{
UNUserNotificationCenter.Current.RequestAuthorization(
UNAuthorizationOptions.Alert |
UNAuthorizationOptions.Badge |
UNAuthorizationOptions.Sound,
(approvalGranted, error) =>
{
if (approvalGranted && error == null)
RegisterForRemoteNotifications();
});
}
LoadApplication(new App());
using (var userInfo = options?.ObjectForKey(
UIApplication.LaunchOptionsRemoteNotificationKey) as NSDictionary)
ProcessNotificationActions(userInfo);
return base.FinishedLaunching(app, options);
}
void RegisterForRemoteNotifications()
{
MainThread.BeginInvokeOnMainThread(() =>
{
var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert |
UIUserNotificationType.Badge |
UIUserNotificationType.Sound,
new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
});
}
Task CompleteRegistrationAsync(NSData deviceToken)
{
DeviceInstallationService.Token = deviceToken.ToHexString();
return NotificationRegistrationService.RefreshRegistrationAsync();
}
void ProcessNotificationActions(NSDictionary userInfo)
{
if (userInfo == null)
return;
try
{
var actionValue = userInfo.ObjectForKey(new NSString("action")) as NSString;
if (!string.IsNullOrWhiteSpace(actionValue?.Description))
NotificationActionService.TriggerAction(actionValue.Description);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
=> CompleteRegistrationAsync(deviceToken).ContinueWith((task)
=> { if (task.IsFaulted) throw task.Exception; });
public override void ReceivedRemoteNotification(
UIApplication application,
NSDictionary userInfo)
=> ProcessNotificationActions(userInfo);
public override void FailedToRegisterForRemoteNotifications(
UIApplication application,
NSError error)
=> Debug.WriteLine(error.Description);
}
}
DeviceInstallationService:
using System;
using Notes.Models;
using Notes.Services;
using UIKit;
namespace Notes.iOS.Services
{
public class DeviceInstallationService : IDeviceInstallationService
{
const int SupportedVersionMajor = 13;
const int SupportedVersionMinor = 0;
public string Token { get; set; }
public bool NotificationsSupported
=> UIDevice.CurrentDevice.CheckSystemVersion(SupportedVersionMajor, SupportedVersionMinor);
public string GetDeviceId()
=> UIDevice.CurrentDevice.IdentifierForVendor.ToString();
public DeviceInstallation GetDeviceInstallation(params string[] tags)
{
if (!NotificationsSupported)
throw new Exception(GetNotificationsSupportError());
if (string.IsNullOrWhiteSpace(Token))
throw new Exception("Unable to resolve token for APNS");
var installation = new DeviceInstallation
{
InstallationId = GetDeviceId(),
Platform = "apns",
PushChannel = Token
};
installation.Tags.AddRange(tags);
return installation;
}
string GetNotificationsSupportError()
{
if (!NotificationsSupported)
return $"This app only supports notifications on iOS {SupportedVersionMajor}.{SupportedVersionMinor} and above. You are running {UIDevice.CurrentDevice.SystemVersion}.";
if (Token == null)
return $"This app can support notifications but you must enable this in your settings.";
return "An error occurred preventing the use of push notifications";
}
}
}
As you can see this is really 1:1 the example code, the only difference is that my project is called Notes.
I skipped the Firebase and Android-Part as I only need push-notifications for iOS so far and as far as I underestood these are not necessary for iOS only.
Thanks your help!
Some points to check if RegisteredForRemoteNotification not called:
Open Entitlements.plist and ensure that Enable Push Notifications is checked when viewed in the Entitlements tab. Then, ensure the APS Environment setting is set to development when viewed in the Source tab.
Make sure that you are testing the remote-notification in a real device instead of a simulator. A simulator does not support remote-notification.
Make sure that you agreed receiving notification permission.
Make sure the certification you use has enabled the push notification ability.
Refer: configuring-the-remote-notifications-environment
You can look at the message returned from the following function in your App Delegate AppDelegate.cs
public override void FailedToRegisterForRemoteNotifications(
UIApplication application,
NSError error)
For instance
no valid “aps-environment” entitlement string found for application
I'm integrating an Asp.NET application with Acumatica that needs to update shipping information (tracking #, carrier, etc.) when it becomes available in Acumatica. Is there a way to have Acumatica call an endpoint on my Asp.NET app when a shipment is created? I've searched through a lot of the docs (available here), but I haven't come across anything to send OUT information from Acumatica to another web service.
Ideally, this outgoing call would send the shipment object in the payload.
This wasn't available when you asked the question but push notifications seem to be exactly what you're looking for:
Help - https://help.acumatica.com/(W(9))/Main?ScreenId=ShowWiki&pageid=d8d2835f-5450-4b83-852e-dbadd76a5af8
Presentation - https://adn.acumatica.com/content/uploads/2018/05/Push-Notifications.pdf
In my answer I suppose that you know how to call some outside service from C# code, and for your is a challenge how to send notification from Acumatica.
I propose you to extend each Persist method in each Acumatica graph, from which you expect to send notification when object is persisted in db. IMHO the best option for this is to override method persist ( btw, it overriding persist method is well described in T300 ). In code of extension class you can do the following:
public void Persist(PersistDelegate baseMethod)
{
baseMethod(); // calling this method will preserve your changes in db
//here should go your code, that will send push/pop/delete etc web request into your asp.net application. Or in other words your web hook.
}
If you don't have Acumatica 2017R2, then you have to create your own extension project and then you can call it from your Acumatica code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
namespace MyApp
{
public static class Utility
{
private static WebRequest CreateRequest(string url, Dictionary headers)
{
if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
WebRequest req = WebRequest.Create(url);
if (headers != null)
{
foreach (var header in headers)
{
if (!WebHeaderCollection.IsRestricted(header.Key))
{
req.Headers.Add(header.Key, header.Value);
}
}
}
return req;
}
else
{
throw(new ArgumentException("Invalid URL provided.", "url"));
}
}
public static string MakeRequest(string url, Dictionary headers = null)
{
WebResponse resp = CreateRequest(url, headers).GetResponse();
StreamReader reader = new StreamReader(resp.GetResponseStream());
string response = reader.ReadToEnd();
reader.Close();
resp.Close();
return response;
}
public static byte[] MakeRequestInBytes(string url, Dictionary headers = null)
{
byte[] rb = null;
WebResponse resp = CreateRequest(url, headers).GetResponse();
using (BinaryReader br = new BinaryReader(resp.GetResponseStream()))
{
rb = br.ReadBytes((int)resp.ContentLength);
br.Close();
}
resp.Close();
return rb;
}
}
}
You can then call it like this:
try
{
Utility.MakeRequest(theUrl, anyHeadersYouNeed);
}
catch(System.Net.WebException ex)
{
throw(new PXException("There was an error.", ex));
}
Iam able to register using device token.but I didn't receive message from my server.I have registered my project with GCM and got project Id ,server key.
enter code here
public void onMessage(Event event) {
String msg;
msg = event.getPayload();
System.out.println("#### Message from the Server :" + msg);
String one="";
AdfmfContainerUtilities.invokeContainerJavaScriptFunction(AdfmfJavaUtilities.getFeatureId(), "usercheck", new Object[]{msg});
// Parse the payload of the push notification
HashMap payload = null;
String pushMsg = "No message received";
try
{
payload = (HashMap)JSONBeanSerializationHelper.fromJSON(HashMap.class, msg);
pushMsg = (String)payload.get("alert");
}
catch(Exception e) {
e.printStackTrace();
}
// Write the push message to app scope to display to the user
AdfmfJavaUtilities.setELValue("#{applicationScope.pushMessage}", pushMsg);
}
public void onError(AdfException adfException) {
System.out.println("#### Error: " + adfException.toString());
// Write the error into app scope
AdfmfJavaUtilities.setELValue("#{applicationScope.errorMessage}", adfException.toString());
}
public void onOpen(String token) {
System.out.println("#### Registration token:" + token);
// Clear error in app scope
AdfmfJavaUtilities.setELValue("#{applicationScope.errorMessage}", null);
// Write the token into app scope
AdfmfJavaUtilities.setELValue("#{applicationScope.deviceToken}", token);
}
}
I think this article will help you. I was able to get them done for GCM but not for APN yet.
For registering with Google Cloud Messaging, go to this link, then click on "Get configuration" button.
In the server application, set your device token and sender id properly. The message is sent using these.
Since you got the deviceId from the server then i think the issue is not with the code. Did you enable PushPlugin in maf-application.xml ?
I am currently writing a mobile application with Xamarin for Android and will be adding iOS once my company purchases a Mac so that I can start developing the iOS portion. I am currently trying to write a .Net Mobile Services back-end for Azure Notification Hub that will allow me to register the devices from the back end as well as send push notification to a specific user, and/or all users.
I have followed the Azure documentation all the way through Getting Started With Notification Hub and have successfully performed a single platform push. Moving beyond this example however is where I get stuck. Every example beyond this point completely drops Android support and only focuses on Windows Phone and iOS. I have watched a few Channel 9 videos regarding this subject and again it's all Windows Phone, Windows 8, and iOS based.
Does anyone have an example of a .Net Mobile Service back-end for Azure Notification Hub that will register the device to the notification hub from the back-end? Thank you for your time.
I don't have sample code on GitHub yet, but here's a gist of how to get NotificationHub working on Android.
using Microsoft.ServiceBus.Notifications;
using Newtonsoft.Json;
public class AndroidNotificationHub
{
private readonly NotificationHubClient _hubClient;
public AndroidNotificationHub()
{
const string cn = "YourConnectionStringHere";
const string hubPath = "YourHubPathHere";
_hubClient = NotificationHubClient.CreateClientFromConnectionString(cn, hubPath);
}
public async Task<RegistrationDescription> Register(string platform, string installationId, string registrationId, string userName)
{
// Get registrations for the current installation ID.
var regsForInstId = await _hubClient.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 "android":
var gcmReg = registrationDescription as GcmRegistrationDescription;
gcmReg.GcmRegistrationId = registrationId;
registration = await _hubClient.UpdateRegistrationAsync(gcmReg);
break;
}
updated = true;
firstRegistration = false;
}
else
{
// We shouldn't have any extra registrations; delete if we do.
await _hubClient.DeleteRegistrationAsync(registrationDescription);
}
}
// Create a new registration.
if (!updated)
{
switch (platform)
{
case "android":
registration = await _hubClient.CreateGcmNativeRegistrationAsync(registrationId, new[] { installationId, userName });
break;
}
}
return registration;
}
// Basic implementation that sends a notification to Android clients
public async Task<bool> SendNotification(int id, string from, string text, string tag)
{
try
{
var payload = new
{
data = new
{
message = new
{
// these properties can be whatever you want
id,
from,
text,
when = DateTime.UtcNow.ToString("s") + "Z"
}
}
};
var json = JsonConvert.SerializeObject(payload);
await _hubClient.SendGcmNativeNotificationAsync(json, tag);
return true;
}
catch (ArgumentException ex)
{
// This is expected when an APNS registration doesn't exist.
return false;
}
}
public async Task<bool> ClearRegistrations(string userName)
{
// Get registrations for the current installation ID.
var regsForInstId = await _hubClient.GetRegistrationsByTagAsync(userName, 100);
// Check for existing registrations.
foreach (var registrationDescription in regsForInstId)
{
// We shouldn't have any extra registrations; delete if we do.
await _hubClient.DeleteRegistrationAsync(registrationDescription);
}
return true;
}
}
Your Android client will need to call your backend's registration API during startup. I have an MVC action for this.
[HttpPost]
public async Task<ActionResult> Register(string platform, string installationId, string registrationId, string userName)
{
try
{
var hub = new AndroidNotificationHub();
var registration = await hub.Register(platform, installationId, registrationId, userName);
return Json(registration);
}
catch (Exception ex)
{
return Content(ex.ToString());
}
}
Once the mobile client has registered, you can then start sending notifications from your backend, by calling the SendNotification method.
Hope this points you in the right direction.
Does anyone know why the following method within my app would receive a null deviceToken after registering with APNS (with MonoTouch)?
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken){
//
}
Thanks.
The debugger says that the deviceToken is null but it is not. (At least that is what I observe) You can use the code below to build a string that represents your device token.
public override void RegisteredForRemoteNotifications (UIApplication application, NSData deviceToken)
{
byte [] token = deviceToken.ToArray ();
string tokenString = "";
for (int i=0; i<deviceToken.Length; i++)
tokenString += token[i].ToString ("X2");
Console.WriteLine (tokenString);
}
Here is a good tutorial that explains push notifications. It is for XCode but it is easy to convert.