How to send a push notification to more than one device (iOS)? - push-notification

I'm trying to optimize the push notifications on my server. For now I send those one by one (with an old library) and it takes a while (4 hours).
I refactored my service to send a notification with a lot of device tokens (For now I tried with batches of 500 tokens). For that I'm using the Redth/PushSharp library. I followed the sample code then I adapted it to send the notifications to severals device tokens.
PushService service = new PushService();
//Wire up the events
service.Events.OnDeviceSubscriptionExpired += new PushSharp.Common.ChannelEvents.DeviceSubscriptionExpired(Events_OnDeviceSubscriptionExpired);
service.Events.OnDeviceSubscriptionIdChanged += new PushSharp.Common.ChannelEvents.DeviceSubscriptionIdChanged(Events_OnDeviceSubscriptionIdChanged);
service.Events.OnChannelException += new PushSharp.Common.ChannelEvents.ChannelExceptionDelegate(Events_OnChannelException);
service.Events.OnNotificationSendFailure += new PushSharp.Common.ChannelEvents.NotificationSendFailureDelegate(Events_OnNotificationSendFailure);
service.Events.OnNotificationSent += new PushSharp.Common.ChannelEvents.NotificationSentDelegate(Events_OnNotificationSent);
service.Events.OnChannelCreated += new PushSharp.Common.ChannelEvents.ChannelCreatedDelegate(Events_OnChannelCreated);
service.Events.OnChannelDestroyed += new PushSharp.Common.ChannelEvents.ChannelDestroyedDelegate(Events_OnChannelDestroyed);
//Configure and start ApplePushNotificationService
string p12Filename = ...
string p12FilePassword = ...
var appleCert = File.ReadAllBytes(p12Filename);
service.StartApplePushService(new ApplePushChannelSettings(true, appleCert, p12FilePassword));
var appleNotification = NotificationFactory.Apple();
foreach (var itemToProcess in itemsToProcess)
{
itemToProcess.NotificationDateTime = DateTime.Now;
mobile.SubmitChanges();
string deviceToken = GetCleanDeviceToken(itemToProcess.MobileDevice.PushNotificationIdentifier);
appleNotification.ForDeviceToken(deviceToken);
}
service.QueueNotification(appleNotification
.WithAlert(itemsToProcess[0].MobileDeviceNotificationText.Text)
.WithSound("default")
.WithBadge(0)
.WithCustomItem("View", itemsToProcess[0].Value.ToString()));
//Stop and wait for the queues to drains
service.StopAllServices(true);
Then I tried to send 3 notifications to 2 devices. Only the first device got them (and the problem is not device-related because I tried with both of them separately).
Right after that an OperationCanceledException is thrown in the PushChannelBase class. So I don't know what's wrong. Any idea?

You should queue a separate notification for each item to process.
It is not possible set multiple device tokens on a single notification. The OperationCanceledException will occur, because you do.

Example: Console C# Application
This assumes
you have valid production and development certificates
you have stored multiple device tokens within your database
you have a notification that comes from your database
You are using PushSharp Library
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PushSharp;
using PushSharp.Core;
using PushSharp.Apple;
using System.IO;
namespace MyNotification
{
class Program
{
//args may take "true" or "false" to indicate the app is running for
//development or production (Default = false which means Development)
static void Main(string[] args)
{
bool isProduction = false;
if (args != null && args.Length == 1)
{
Console.Write(args[0] + Environment.NewLine);
bool.TryParse(args[0], out isProduction);
}
try
{
//Gets a notification that needs sending from database
AppNotification notification = AppNotification.GetNotification();
if (notification != null && notification.ID > 0)
{
//Gets all devices to send the above notification to
List<IosDevice> devices = IosDevice.GetDevices(!isProduction);
if (devices != null && devices.Count > 0)
{
PushBroker push = new PushBroker();//a single instance per app
//Wire up the events for all the services that the broker registers
push.OnNotificationSent += NotificationSent;
push.OnChannelException += ChannelException;
push.OnServiceException += ServiceException;
push.OnNotificationFailed += NotificationFailed;
push.OnDeviceSubscriptionExpired += DeviceSubscriptionExpired;
push.OnChannelCreated += ChannelCreated;
push.OnChannelDestroyed += ChannelDestroyed;
//make sure your certifcates path are all good
string apnsCertFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../Certificate/Certificates_Apple_Push_Production.p12");
if (!isProduction)
apnsCertFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../Certificate/Certificates_Apple_Push_Development.p12");
var appleCert = File.ReadAllBytes(apnsCertFile);
push.RegisterAppleService(new ApplePushChannelSettings(isProduction, appleCert, "135TrID35")); //Extension method
foreach (IosDevice device in devices)
{
//if it is required to send additional information as well as the alert message, uncomment objects[] and WithCustomItem
//object[] obj = { "North", "5" };
push.QueueNotification(new AppleNotification()
.ForDeviceToken(device.DeviceToken)
.WithAlert(DateTime.Now.ToString())//(notification.AlertMessage)
//.WithCustomItem("Link", obj)
.WithBadge(device.BadgeCount + 1)
.WithSound(notification.SoundFile));//sound.caf
}
push.StopAllServices(waitForQueuesToFinish: true);
}
}
Console.WriteLine("Queue Finished, press return to exit...");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
static void NotificationSent(object sender, INotification notification)
{
Console.WriteLine("Sent: " + sender + " -> " + notification);
}
static void NotificationFailed(object sender, INotification notification, Exception notificationFailureException)
{
Console.WriteLine("Failure: " + sender + " -> " + notificationFailureException.Message + " -> " + notification);
}
static void ChannelException(object sender, IPushChannel channel, Exception exception)
{
Console.WriteLine("Channel Exception: " + sender + " -> " + exception);
}
static void ServiceException(object sender, Exception exception)
{
Console.WriteLine("Service Exception: " + sender + " -> " + exception);
}
static void DeviceSubscriptionExpired(object sender, string expiredDeviceSubscriptionId, DateTime timestamp, INotification notification)
{
Console.WriteLine("Device Subscription Expired: " + sender + " -> " + expiredDeviceSubscriptionId);
}
static void ChannelDestroyed(object sender)
{
Console.WriteLine("Channel Destroyed for: " + sender);
}
static void ChannelCreated(object sender, IPushChannel pushChannel)
{
Console.WriteLine("Channel Created for: " + sender);
}
}
}

Related

Cannot open passkit file created in the server using dotnet-passbook v3.0.2

I have recently come across an issue that I cannot open my Pkpass which is created using dotnet-passbook version 3.2.0.
The Pkpass created locally can be opened fine and the issue is with the Pkpass created in the server.
I have even tried to upload the Pkpass from the server to the validator but the validator just returns “Failed to process the Pkpass file.”
Below is my code snapshot for building the Pkpass:
public static FileContentResult GetPass(Shopper account)
{
try {
if (account == null)
{
return null;
}
PassGenerator generator = new PassGenerator();
PassGeneratorRequest request = new PassGeneratorRequest();
// "webServiceURL" : "https://example.com/passes/",
// "authenticationToken" : "vxwxd7J8AlNNFPS8k0a0FfUFtq0ewzFdc",
// Product info
request.PassTypeIdentifier = "PassTypeIdentifier";
request.TeamIdentifier = "TeamIdentifier";
request.SerialNumber = "SerialNumber";
request.Description = "Description";
request.OrganizationName = "OrganizationName";
request.LogoText = "LogoText";
// Add the installed certificate and it's thumbprint
WebClient webClient = new WebClient();
try
{
X509KeyStorageFlags flags = X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable;
request.PassbookCertificate = new X509Certificate2(webClient.DownloadData(CertificatesBucket + "/Certificates.p12"), S3_AppleCertificatePassword, flags);
}
catch (Exception e)
{
throw new Exception("Error occurs when geting certificate: " + e.Message);
}
// Add Apple's WWDRCA certificates
try
{
request.AppleWWDRCACertificate = new X509Certificate2(webClient.DownloadData(CertificatesBucket + "/AppleWWDRCAG2.cer"));
}
catch (Exception e)
{
throw new Exception("Error occurs when geting AppleWWDRCCA: " + e.Message);
}
// override icon and icon retina
try
{
request.Images.Add(PassbookImage.Logo, webClient.DownloadData(AppleImageBucket + "/logo.png"));
request.Images.Add(PassbookImage.Icon, webClient.DownloadData(AppleImageBucket + "/icon.png"));
request.Images.Add(PassbookImage.Icon2X, webClient.DownloadData(AppleImageBucket + "/icon%402x.png"));
request.Images.Add(PassbookImage.Strip, webClient.DownloadData(AppleImageBucket + "/strip.png"));
request.Images.Add(PassbookImage.Strip2X, webClient.DownloadData(AppleImageBucket + "/strip%402x.png"));
}
catch (Exception e)
{
throw new Exception("Error occurs when geting images: " + e.Message);
}
// Add barcode
request.AddBarcode(BarcodeType.PKBarcodeFormatCode128, account.Id, "ISO-8859-1");
// Link the pass to an existing app using the app's Apple ID.
request.AssociatedStoreIdentifiers.Add(121212);
request.Style = PassStyle.StoreCard;
// Add fields
request.AddHeaderField(new StandardField("StandardField", "TEXT", account.Id));
request.AddSecondaryField(new StandardField("StandardField", "TEXT", account.Name));
request.AddSecondaryField(new StandardField("StandardField", "TEXT", "TEXT"));
request.TransitType = TransitType.PKTransitTypeAir;
byte[] generatedPass = generator.Generate(request);
//throw new Exception("apple generatedPass: " + JsonTransHelper.SerializeObject(generatedPass));
return new FileContentResult(generatedPass, "application/vnd.apple.pkpass");
}
catch (Exception ex)
{
throw new Exception("Error occurs when creating apple pass: " + ex.Message);
}
}
}
}
Any help greatly appreciated!
It is caused by AWS API Gateway. If you running this on a Lambda function try to invoke it directly.
Offset to Central Directory cannot be held in an Int64 .net core

xamarin forms ios camera crash issue

I have a xamarin forms cross platform application. In that application i am using a xamarin forms dependency service to take photo from iOS and android device on native platform. Android camera working fine but iOS camera giving error and app crash suddenly. I log and trace issues of iOS camera as following.
** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller .'
Following are my iOS native code used to take photo and it returns photo stream as result.I call this method or service from my shared xamarin form project using dependency service.
public Task<CameraResult> TakePictureAsync(bool cameraFlag) { var tcs = new TaskCompletionSource<CameraResult>();
Camera.TakePicture(UIApplication.SharedApplication.KeyWindow.RootViewController, (imagePickerResult) =>
{
if (imagePickerResult == null)
{
tcs.TrySetResult(null);
return;
}
var photo = imagePickerResult.ValueForKey(new NSString("UIImagePickerControllerOriginalImage")) as UIImage;
// You can get photo meta data with using the following
// var meta = obj.ValueForKey(new NSString("UIImagePickerControllerMediaMetadata")) as NSDictionary;
var documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string imageName = Settings.AppuserUserId + "_" + Guid.NewGuid();
string jpgFilename = Path.Combine(documentsDirectory, imageName + ".jpg");
NSData imgData = photo.AsJPEG();
NSError err = null;
if (imgData.Save(jpgFilename, false, out err))
{
CameraRes
ult result = new CameraResult();
result.Picture = ImageSource.FromStream(imgData.AsStream);
result.FilePath = jpgFilename;
tcs.TrySetResult(result);
}
else
{
tcs.TrySetException(new Exception(err.LocalizedDescription));
} });
return tcs.Task; }
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
CameraResult result = new CameraResult();
NSData data = image.AsJPEG();
result.Picture = ImageSource.FromStream(data.AsStream);
taskCompletionSource.SetResult(result);
}
else
{
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
}
Please share any solution for this issue and error.

Whats wrong with this Async HystrixCommand?

I need to send notifications from time to time, I perform this task asynchronously. I'm using HystrixCommand as below to perform an asynchronous RestTemplate call which is not working:
#HystrixCommand
public Future<String> notify(final Query query) {
return new AsyncResult<String>() {
#Override
public String invoke() {
String result = null;
try {
ResponseEntity<HashMap> restExchange = restTemplate.exchange(url,
HttpMethod.POST,
new HttpEntity<String>(mapper.writeValueAsString(queryMap), httpHeaders),
HashMap.class);
LOGGER.info("Response code from " + url + " = " + restExchange.getStatusCodeValue());
result = ""+ restExchange.getStatusCodeValue();
} catch(Exception e) {
LOGGER.error("Exception while sending notification! Message = " + e.getMessage(), e);
}
return result;
}
};
}
This is what I was trying to do earlier(which didn't work either):
#HystrixCommand
public String notify(final Query query) {
new Thread(new Runnable() {
#Override
public void run() {
try {
ResponseEntity<HashMap> restExchange = restTemplate.exchange(url, HttpMethod.POST,
new HttpEntity<String>(mapper.writeValueAsString(queryMap), httpHeaders), HashMap.class);
LOGGER.info("Response code from " + url + " = " + restExchange.getStatusCodeValue());
} catch (Exception e) {
LOGGER.error("Exception while sending notification! Message = " + e.getMessage(), e);
}
}
}).start();
}
P.S: Reason for adding sleuth to the tags is, performing this in a new Thread does not propagate the headers(baggage-*) so trying this hoping the Hystrix command will do the trick
Is the method notify being called from a method in the same class? If that is the case, try calling the method notify directly from a different class where the notify method's enclosing class is injected as a dependency.
Basically, try doing this:
Instead of this:
When using Runnable you have to wrap them in a trace representation. i.e. TraceRunnable. It's there in the docs - http://cloud.spring.io/spring-cloud-sleuth/spring-cloud-sleuth.html#_runnable_and_callable .
As for why the Hystrix stuff doesn't work - most likely it's related to https://github.com/spring-cloud/spring-cloud-sleuth/issues/612 .

Push Notification, alert message not receiving on iPhone

I am working on Push notifications for iPhone. I am sending notification on button click, but the iPhone developer is not receiving the alert message. Can anyone plz help me?
This is my method --
protected void btnPush_Click(object sender, EventArgs e)
{
string devicetoken = "bhsdse78 d52c6a34 273de5f7 27947945 24736e36 33d93a6c 3147a416 434995eb";
try
{
if (devicetoken != "")
{
//lblError.Visible = false;
string p12FileName = "D:/Worksapace/Coupzila/Certificates(3).p12"; // change this to reflect your own certificate
string p12Password = "seas"; // change this
bool sandBox = true;
int numConnections = 1; // you can change the number of connections here
var notificationService = new NotificationService(sandBox, p12FileName, p12Password, numConnections);
var deviceToken = devicetoken; // put in your device token here
var notification = new Notification(deviceToken);
notification.Payload.Alert.Body = "Hi this is push notification test message.";
notification.Payload.Sound = "default";
notification.Payload.Badge = 1;
notification.Payload.HideActionButton = true;
if (notificationService.QueueNotification(notification))
{ }
else
{ }
// This ensures any queued notifications get sent befor the connections are closed
notificationService.Close();
notificationService.Dispose();
}
else
{
//lblError.Visible = true;
}
}
catch (Exception ex)
{
}
}
This method is running successfully, but not getting notifications on iPhone, Whtz problem in my code?
Help me out, Thanks in advance
First, Remove the empty characters from your deviceToken.
Second, Put # sign for your fileName;
string p12FileName = #"D:/Worksapace/Coupzila/Certificates(3).p12";
And for the last step implement your catch, like;
catch (Exception ex)
{
MessageBox.Show("There is an error from push notification. Details:\n" + ex);
}
And good with Apple.

Wcf + asp.net + multiple threads

I have the following scenario:
A Service that does nothing but sleeps for the amount of time the WPF specifies through the WebRequest onject:
public class WorkerSvc : IWorkerSvc
{
#region IWorkerSvc Members
public void DoWork(int timeToSleep)
{
Trace.WriteLine(DateTime.Now.ToString() + "\tInside Stress Service with TimeToSleep: " + timeToSleep.ToString());
if (timeToSleep == 0)
return;
Thread.Sleep(timeToSleep * 1000);
Trace.WriteLine(DateTime.Now.ToString() + "\tThe Thread woke up.");
}
Then an aspx page (separate project) that calls into the service:
protected void Page_Load(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + " \tInside default stress web site page load;");
using (WorkerService.WorkerSvcClient client = new StressWebSite.WorkerService.WorkerSvcClient())
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + " \tCreated a new client of Stress WCF Service;");
var rowdata = Request.QueryString["SleepValue"];
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + " \tDetected Sleep Value in the Request: " + rowdata);
int realData = 0;
if (!string.IsNullOrEmpty(rowdata))
{
if (int.TryParse(rowdata, out realData))
{
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + " \tBefore Dowork() with SleepValue: " + realData);
client.DoWork(realData);
System.Diagnostics.Trace.WriteLine(DateTime.Now.ToString() + " \tAfter DoWork() with SleepValue: " + realData);
Response.Write(realData.ToString());
//Response.End();
}
}
}
}
A WPF Form That spanws a number of threads which essentially post some data to an aspx page:
private void PerformStress()
{
WebRequest request = WebRequest.Create(string.Concat(this.txtStressPageUrl.Text, "?", "SleepValue", "=", this.txtSleepValue.Text));
Trace.WriteLine("Created Web Request");
request.Credentials = CredentialCache.DefaultCredentials;
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
//return responseFromServer;
}
private void button3_Click(object sender, RoutedEventArgs e)
{
int stressThreadsCount = 0;
if (!int.TryParse(this.txtStressThreadCount.Text, out stressThreadsCount))
MessageBox.Show("Enter number of threads to stress");
ThreadStart start = delegate()
{
DispatcherOperation op = Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(PerformStress));
DispatcherOperationStatus status = op.Status;
while (status != DispatcherOperationStatus.Completed)
{
status = op.Wait(TimeSpan.FromMilliseconds(1000));
if (status == DispatcherOperationStatus.Aborted)
{
//
}
}
};
for (int i = 0; i < stressThreadsCount; i++)
{
var t = new Thread(start);
//this.runningThreads.Add(t);
t.Start();
//t.Join();
}
}
The problem is that it looks like eventhough I spawn several threads from the WPF side, to post to the aspx page, all the threads are processed in a serial manner, meaning each thread is waiting for the service to return.
I run this set up under cassiny.
I did not specify the WCF service's behavior to be a singleton.
Please help to ID the issue - I'd like to simulate multiple requests to the IIS and a page that calls into WCF to prove a concept.
Singleton will only affect how many objects will be created to service requests. By default, though, WCF services will only process a single request at a time. To change that, you should decorate your service implementation with
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
For more info, see here: http://msdn.microsoft.com/en-us/library/system.servicemodel.concurrencymode.aspx

Resources