I'm setting up push notifications in a Xamarin.Forms project. I already did everything for Xamarin.Forms.Android and it works without any problem but I'm getting a lot of troubles with the iOS part. This isn't even the first time I do this but I still can't figure out what's going on.
What I did:
1. Included Nuget Xamarin.Firebase.iOS.CloudMessaging v3.1.2 (which is not the latest, but the latest is not even building because of library errors)
2. Create Firebase application and follow the usual setup, uploaded my .p12 for push notifications and added my Team ID.
3. Added my GoogleService-Info.plist and set it's BuildAction to "BundleResource"
4. Updated my Info.plist
5. Made sure that my App ID in Apple Developer Program included Push Notifications and the correct certificate
6. Added every piece of code I could related to the UserNotifications.IUNUserNotificationCenterDelegate, IMessagingDelegate interfaces
7. Retrieved my token from
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var token = Messaging.SharedInstance.FcmToken;
if (!string.IsNullOrEmpty(token))
{
//AppData.Instance.TokenMobile = newToken;
}
});
Went back to Firebase console, created a test push and tried to send it both via "Invia messagio di prova" ("Send message") which made me specify a single token and via publishing.
My AppDelegate.cs
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, UserNotifications.IUNUserNotificationCenterDelegate, IMessagingDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
global::Xamarin.Forms.FormsMaterial.Init();
LoadApplication(new App());
Firebase.Core.App.Configure();
// For iOS 10 data message (sent via FCM)
Messaging.SharedInstance.Delegate = this;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = this;
// iOS 10 or later
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
if (granted)
{
InvokeOnMainThread(() =>
{
UIApplication.SharedApplication.RegisterForRemoteNotifications();
});
}
});
}
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();
// Handle token as you wish
Firebase.InstanceID.InstanceId.Notifications.ObserveTokenRefresh((sender, e) =>
{
var token = Messaging.SharedInstance.FcmToken;
if (!string.IsNullOrEmpty(token))
{
//AppData.Instance.TokenMobile = newToken;
}
});
UIApplication.SharedApplication.StatusBarHidden = true;
UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
return base.FinishedLaunching(app, options);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
}
[Export("messaging:didRefreshRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
Console.WriteLine($"Firebase registration token: {fcmToken}");
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
//Tested
//Messaging.SharedInstance.ApnsToken = deviceToken;
}
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
var userInfo = notification.Request.Content.UserInfo;
// With swizzling disabled you must let Messaging know about the message, for Analytics
// Messaging.SharedInstance.AppDidReceiveMessage (userInfo);
// Print full message.
Console.WriteLine(userInfo);
// Change this to your preferred presentation option
completionHandler(UNNotificationPresentationOptions.None);
}
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action boh)
{
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage message)
{
}
}
I also added this key to the Entitlment.plist file:
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
At this point I was expecting to receive something in my app, but I can't receive anything.
I put some breakpoints in each callback I tried to implement but none of them is getting called.
If it can be helpful, not even "DidReceiveRegistrationToken" method is getting called.
Solved the problem.
I don't know exactly why but I found in my GoogleService-Info.plist a different API Key from the one specified in the Firebase Console. I updated the key and I'm now receiving notifications.
Related
I am trying to register a physical iOS (13) device for remote notifications using Xamarin.iOS in a Xamarin.Forms project.
After it worked perfectly fine for about two weeks, I face the issue that after I call
UIApplication.SharedApplication.RegisterForRemoteNotifications()
(See complete code below)
Neither RegisteredForRemoteNotifications nor FailedToRegisterForRemoteNotifications of the AppDelegate is called.
The only thing that changed is that I added a production APNS-Certificate to my app-id that now looks like this:
App id redacted with xxxx
I can select the associated development-provisioning profile in Xcode and I can deploy the app without errors.
The relevant code from the AppDelgate:
FinishedLaunching:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Forms.SetFlags("IndicatorView_Experimental", "SwipeView_Experimental");
Forms.Init();
//other init methods omitted
LoadApplication(new App());
var baseFinished = base.FinishedLaunching(app, options);
RegisterForRemoteNotifications();
return baseFinished;
}
RegisterForRemoteNotifications:
public void RegisterForRemoteNotifications()
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
var options = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound |
UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(options,(granted, error) =>
{
if (granted)
{
//this method is getting called
InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);
}
});
}
//checking for other iOS versions omitted
}
RegisteredForRemoteNotifications and FailedToRegisterForRemoteNotifications - Both are not getting called:
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
var bytes = deviceToken.ToArray();
var token = BitConverter.ToString(bytes).Replace("-", "");
Debug.WriteLine($"Got iOS notification token: {token}");
DependencyService.Get<INotificationRegistrationService>().OnTokenAcquired(token, DeviceType.Apple);
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
Debug.WriteLine("error on register remote notifications ");
}
I may have just made a mistake in the code or with the certificates.
For anyone with the same problem, try using a mobile hotspot / mobile internet to register the iPhone. This is what solved it for me
I guess my firewall somehow blocked communication with the apns server.
I have no clue why because it worked before.
Now I will investigate how exactly the communication got blocked and if I have any relevant information, I will post them here.
I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I am doing it in C# and on the online code editor of Microsoft Azure.
Unfortunately, I can not connect to the translation layer (at least it looks like that).
When I tried to debug it, I can see that it stops at this specific part:
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
I checked the network timeout errors and there are many (20). All of them say "There was an error sending this message to your bot: HTTP status code GatewayTimeout".
I can "build.cmd" normally, without any errors, and when I try to do Debug.WriteLine or Console.WriteLine, nothing is printed out (I even tried in VS and Emulator)
The only thing that I am doing differently, compared to the above link, is that I defined the "host" and "key" outside of the private method:
private static async Task<string> TranslateQuestionToEnglish (...)
So, I take any word and want to translate it into English.
When I take out those two lines of the code, and test a method with static values, it obviously works (all together with QnA and everything else).
Later on, I am calling this method in "Task MessageReceivedAsync".
I created a translation cognitive service, and the only thing that I took from there is the first key from "Keys" and used it here in this method. Is that the only thing that I need from created cognitive service??
Another thing that I am not sure about, and if that thing is making a problems is that when I go to all resources, I can see that my qnatestbot(web app bot) and translator_test(cognitive services) are of type "global" location, while my qnatestbot(app service) is of type "west europe" location. Can the thing that they are in different regions make a problems? Should I put all of them in West Europe (since I am in Germany)?
Although, now that I look at the translator_test(cognitive services) endpoint, I can see that it is ...api.congitivemicrosft.com/.../v1.0.
But, when I was creating a resource it was automatically created like this, without specifying it from my side? How can I change that?
I hope that somebody successfully came across such an issue and can help me. Thank you in advance
I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I try to create a sample to achieve your requirement: translate user inputs to English and pass translation text to QnAMaker dialog, the sample works fine both on local and Azure, you can refer to it.
In MessagesController:
[BotAuthentication]
public class MessagesController : ApiController
{
static string uri = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en";
static string key = "{the_key}";
/// <summary>
/// POST: api/Messages
/// receive a message from a user and send replies
/// </summary>
/// <param name="activity"></param>
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
// check if activity is of type message
if (activity.GetActivityType() == ActivityTypes.Message)
{
if (activity.Text != null)
{
var textinEN = await TranslateQuestionToEnglish(activity.Text);
activity.Text = textinEN;
}
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
private static async Task<string> TranslateQuestionToEnglish(string text)
{
System.Object[] body = new System.Object[] { new { Text = text } };
var requestBody = JsonConvert.SerializeObject(body);
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(uri);
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
request.Headers.Add("Ocp-Apim-Subscription-Key", key);
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
dynamic jsonResponse = JsonConvert.DeserializeObject(responseBody);
var textinen = jsonResponse[0]["translations"][0]["text"].Value;
return textinen;
}
}
private Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}
}
In dialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
/* Wait until the first message is received from the conversation and call MessageReceviedAsync
* to process that message. */
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
/* When MessageReceivedAsync is called, it's passed an IAwaitable<IMessageActivity>. To get the message,
* await the result. */
var message = await result;
var qnaAuthKey = GetSetting("QnAAuthKey");
var qnaKBId = Utils.GetAppSetting("QnAKnowledgebaseId");
var endpointHostName = Utils.GetAppSetting("QnAEndpointHostName");
// QnA Subscription Key and KnowledgeBase Id null verification
if (!string.IsNullOrEmpty(qnaAuthKey) && !string.IsNullOrEmpty(qnaKBId))
{
// Forward to the appropriate Dialog based on whether the endpoint hostname is present
if (string.IsNullOrEmpty(endpointHostName))
await context.Forward(new BasicQnAMakerPreviewDialog(), AfterAnswerAsync, message, CancellationToken.None);
else
await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
}
else
{
await context.PostAsync("Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.");
}
}
private async Task AfterAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
// wait for the next user message
context.Wait(MessageReceivedAsync);
}
public static string GetSetting(string key)
{
var value = Utils.GetAppSetting(key);
if (String.IsNullOrEmpty(value) && key == "QnAAuthKey")
{
value = Utils.GetAppSetting("QnASubscriptionKey"); // QnASubscriptionKey for backward compatibility with QnAMaker (Preview)
}
return value;
}
}
// Dialog for QnAMaker Preview service
[Serializable]
public class BasicQnAMakerPreviewDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: subscriptionKey, knowledgebaseId,
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerPreviewDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5)))
{ }
}
// Dialog for QnAMaker GA service
[Serializable]
public class BasicQnAMakerDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: qnaAuthKey, knowledgebaseId, endpointHostName
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5, 1, Utils.GetAppSetting("QnAEndpointHostName"))))
{ }
}
Test result:
Note: We can use ConfigurationManager.AppSettings["QnAKnowledgebaseId"]; to access QnAKnowledgebaseId etc settings from web.config if run bot application on local. For more information, please refer to this SO thread.
I have an ASP.NET Boilerplate v3.6.2 project (.NET Core / Angular) in which I need to call a client function from a backend method, so I'm using the ASP.NET Core SignalR implementation.
I followed the official documentation, so:
In the backend
In my module, I added the dependency to AbpAspNetCoreSignalRModule:
[DependsOn(typeof(AbpAspNetCoreSignalRModule))]
public class MyModule : AbpModule
And added this NuGet package to the module's project.
Then I extended the AbpCommonHub class to take advantage of the built-in implementation of the SignalR Hub, adding a SendMessage() method used to send the message:
public interface IMySignalRHub : ITransientDependency
{
Task SendMessage(string message);
}
public class MySignalRHub: AbpCommonHub, IMySignalRHub {
protected IHubContext<MySignalRHub> _context;
protected IOnlineClientManager onlineClientManager;
protected IClientInfoProvider clientInfoProvider;
public MySignalRHub(
IHubContext<MySignalRHub> context,
IOnlineClientManager onlineClientManager,
IClientInfoProvider clientInfoProvider)
: base(onlineClientManager, clientInfoProvider) {
AbpSession = NullAbpSession.Instance;
_context = context;
}
public async Task SendMessage(string message) {
await _context.Clients.All.SendAsync("getMessage", string.Format("User {0}: {1}", AbpSession.UserId, message));
}
}
I changed the mapping of the '/signalr' url to MySignalRHub:
public class Startup {
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
[...]
# if FEATURE_SIGNALR
app.UseAppBuilder(ConfigureOwinServices);
# elif FEATURE_SIGNALR_ASPNETCORE
app.UseSignalR(routes => {
routes.MapHub<MySignalRHub>("/signalr");
});
# endif
[...]
}
}
Then I use the hub to send a message in a service implementation:
public class MyAppService: AsyncCrudAppService<MyEntity, MyDto, int, PagedAndSortedResultRequestDto, MyCreateDto, MyDto>, IMyAppService {
private readonly IMySignalRHub _hub;
public MyAppService(IRepository<MyEntity> repository, IMySignalRHub hub) : base(repository) {
_hub = hub;
}
public override Task<MyDto> Create(MyCreateDto input) {
_hub.SendMessage("test string").Wait();
[...]
}
}
In the client
All the configurations and inclusions are already in place from the original template. When I open the Angular app I can see this console logs:
DEBUG: Connected to SignalR server!
DEBUG: Registered to the SignalR server!
When I try to call the backend service which sends the message to the client, I get this warning in console:
Warning: No client method with the name 'getMessage' found.
I tried many solutions found in the official documentation and on the Internet but none of them worked. I'm not able to define the 'getMessage' handler in the client code.
Some non-working examples I tried:
Implementation 1:
// This point is reached
abp.event.on('getMessage', userNotification => {
debugger; // Never reaches this point
});
Implementation 2:
// This point is reached
abp.event.on('abp.notifications.received', userNotification => {
debugger; // Never reaches this point
});
Implementation 3:
// This is taken from the official documentation and triggers the error:
// ERROR TypeError: abp.signalr.startConnection is not a function
abp.signalr.startConnection('/signalr', function (connection) {
connection.on('getMessage', function (message) {
console.log('received message: ' + message);
});
});
Have you ever found yourself in this situation? Do you have a simple working example of the handler definition in the Angular client?
UPDATE
I tried this alternative solution, changing the implementation of the SignalRAspNetCoreHelper class (a shared class which is shipped with the base template):
export class SignalRAspNetCoreHelper {
static initSignalR(): void {
var encryptedAuthToken = new UtilsService().getCookieValue(AppConsts.authorization.encrptedAuthTokenName);
abp.signalr = {
autoConnect: true,
connect: undefined,
hubs: undefined,
qs: AppConsts.authorization.encrptedAuthTokenName + "=" + encodeURIComponent(encryptedAuthToken),
remoteServiceBaseUrl: AppConsts.remoteServiceBaseUrl,
startConnection: undefined,
url: '/signalr'
};
jQuery.getScript(AppConsts.appBaseUrl + '/assets/abp/abp.signalr-client.js', () => {
// ADDED THIS
abp.signalr.startConnection(abp.signalr.url, function (connection) {
connection.on('getMessage', function (message) { // Register for incoming messages
console.log('received message: ' + message);
});
});
});
}
}
Now in the console I can see both the messages:
Warning: No client method with the name 'getMessage' found.
SignalRAspNetCoreHelper.ts:22 received message: User 2: asd
So it is working, but not fully. Somewhere the 'getMessage' handler is not visible.
What is the proper way to implement the messages handler in Angular with ASP.NET Boilerplate?
You should use Clients.Others.SendAsync or Client.AllExcept.SendAsync instead of Clients.All.SendAsync.
Clients.All.SendAsync is designed for scenarios where the client wants to send AND receive messages (like a chat room). Hence all connected clients are supposed to implement connection.on('getMessage', in order to receive the notifications. If they don't, they raise the warning No client method with the name 'x' found when receiving back the notification they just pushed.
In your scenario, I understand you have one kind of client pushing notifications and another kind receiving them (like a GPS device sending positions to a tracking application). In that scenario, using Clients.Others.SendAsync or Client.AllExcept.SendAsync will ensure pushing clients will not be broadcasted back the notification they (or their kind) just pushed.
Set autoConnect: false to start your own connection:
abp.signalr = {
autoConnect: false,
// ...
};
Better yet...
Don't extend AbpCommonHub. You'll find that real-time notification stops working and you need to replace IRealTimeNotifier because SignalRRealTimeNotifier uses AbpCommonHub.
Do you have a simple working example of the handler definition in the Angular client?
What is the proper way to implement the messages handler in Angular with ASPNet Boilerplate?
Follow the documentation and create a separate hub.
I was facing the same error in my Angular application where I use the package "#aspnet/signalr": "1.1.4".
The cause of this issue was I wasn't calling the subscribe method after join the channel.
public async getWorkSheetById(worksheetId: string): Promise < Worksheet > {
const worksheet: Worksheet = await this._worksheetService.getWorksheet(worksheetId);
this.displayWorksheet(worksheet);
await this._worksheetEventsService.joinWorksheetChannel(this._loadedWorksheet.id);
return worksheet;
}
So, to fix this I called the subscribe method after join await this.subscribeToSendMethod(this._loadedWorksheet))
public subscribeToSendMethod(loadedWorksheet: Worksheet): Worksheet {
let newWorksheet: Worksheet;
this._hubConnection.on('Send', (groupId: string, payloadType: string, payload: string, senderUserId: string)=> {
newWorksheet=this._worksheetHandler.handlePayload(payloadType, payload, loadedWorksheet);
this.displayWorksheet(newWorksheet);
}
);
return newWorksheet;
}
In my app, for debugging purposes and testing purposes I need to send an email.
How do I present the mail controller, when the class that sends it does not contain a definition for PresentViewController ? The class needs to fire off the email app after the user clicks "yes" from the alert which fires.
public async Task<bool> SendEmail(Exception ex)
{
var result = await SendNotificationToRequestSendingEmail();
if (result)
{
if (MFMailComposeViewController.CanSendMail)
{
mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { "test#one.com", "test#two.com" });
mailController.SetSubject("Details");
mailController.SetMessageBody($"Message: {ex.Message}" +
$"Exception: {ex.ToString()}"
, false);
mailController.Finished += (object s, MFComposeResultEventArgs args) =>
{
Console.WriteLine(args.Result.ToString());
args.Controller.DismissViewController(true, null);
};
this.PresentViewController(mailController, true, null); //this line causes the error
}
}
return true;
}
How can I fix this problem or get around it? This is called from a Xamarin Forms page.
Please take a look at this answer:
Sending e-mail from Gmail in Xamarin.Forms app
besides that you can also do it with this NuGet package:
https://www.nuget.org/packages/Xam.Plugins.Messaging/
we're developing a cross platform(ios and android) app on Xamarin using Xamarin.Forms. We've managed to get the same application working on IOS and Android. Great!
We would like to include push notifications in our app and this already works in Android. For IOS however, it's a completely different story.. No bare bones library to be found, Anywhere!!
For android we used the Google Cloud Messaging Client by Redth, this libary is so easy to use. We had it running in 2 hours or less. However, Nothing to be found anything like this for IOS.
How do i register my IOS device for push notifications in xamarin? We already have the right certificates, etc. its only the device side we need to get working. Something to point me in the right direction?
Use this push notification link for getting or enabling push notification services in IOS.
You will need to do following steps:
1> Create and download SSL and APNS certificates and push notification enabled provisioning profile.
2> First double click on SSL certificates than APNS certificates than provisioning profile.
3.> Now export p12 file from key-chain access and create PEM file from command prompt.
4> Now Register for push notification within FinishedLaunching.
5.> Run your program , You will get a device token and send it to the server.
Now APNS server would send this notification respective to the token.
You might want to look into PushSharp to power the server side part of the iOS and Android notifications. I believe they also have a Xamarin.iOS library you can use to subscribe to the push notifications.
// Register for push notifications.
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
var authOptions = UserNotifications.UNAuthorizationOptions.Alert | UserNotifications.UNAuthorizationOptions.Badge | UserNotifications.UNAuthorizationOptions.Sound;
UserNotifications.UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) =>
{
Console.WriteLine(granted);
});
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
var notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
}
private SBNotificationHub Hub { get; set; }
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
Hub = new SBNotificationHub(Constants.ListenConnectionString, Constants.NotificationHubName);
//if user is not logged In
Employee employee = JsonConvert.DeserializeObject<Employee>(Settings.CurrentUser);
if (employee != null)
{
NSSet tags = new NSSet(new string[] { "username:" + employee.Email }); // create tags if you want
Hub.RegisterNativeAsync(deviceToken, tags, (errorCallback) =>
{
if (errorCallback != null)
Console.WriteLine("RegisterNativeAsync error: " + errorCallback.ToString());
});
}
else
{
Hub.UnregisterAllAsync(deviceToken, (error) =>
{
if (error != null)
{
Console.WriteLine("Error calling Unregister: {0}", error.ToString());
return;
}
});
}
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
AzurePushNotificationManager.RemoteNotificationRegistrationFailed(error);
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}
void ProcessNotification(NSDictionary options, bool fromFinishedLaunching)
{
// Check to see if the dictionary has the aps key. This is the notification payload you would have sent
if (null != options && options.ContainsKey(new NSString("aps")))
{
//Get the aps dictionary
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
string alert = string.Empty;
if (aps.ContainsKey(new NSString("alert")))
alert = (aps[new NSString("alert")] as NSString).ToString();
if (!fromFinishedLaunching)
{
//Manually show an alert
if (!string.IsNullOrEmpty(alert))
{
NSString alertKey = new NSString("alert");
UILocalNotification notification = new UILocalNotification();
notification.FireDate = NSDate.Now;
notification.AlertBody = aps.ObjectForKey(alertKey) as NSString;
notification.TimeZone = NSTimeZone.DefaultTimeZone;
notification.SoundName = UILocalNotification.DefaultSoundName;
UIApplication.SharedApplication.ScheduleLocalNotification(notification);
}
}
}
}