1
votes

We use PushSharp 4.0.10 to send iOS Push Notifications: https://github.com/Redth/PushSharp

Recently we recieved this email from Apple Developer:

"If you still send push notifications with the legacy binary protocol, it's time to update to the HTTP/2-based Apple Push Notification service (APNs) provider API. You'll be able to take advantage of great features, such as authentication with a JSON Web Token, improved error messaging, and per-notification feedback. To give you additional time to prepare, the deadline to upgrade to the APNs provider API has been extended to March 31, 2021. We recommend upgrading as soon as possible, as APNs will no longer support the legacy binary protocol after this date."

My question is: Will PushSharp 4.0.10 still work after March 31, 2021?

1

1 Answers

1
votes

There is a discussion about this but the thread was closed. But there are still some suggestions on this thread that you might want to try.

The Apple Push Notification service (APNs) will no longer support the legacy binary protocol as of November 2020 https://github.com/Redth/PushSharp/issues/923

** EDIT - 25th March 2021

The deadline is close and @Ashita Shah asked some code snippet so I hope the following can save your time.

Add the following class dotAPNSService to your project. You can customise this structure according to your needs. Also I didn't focus the best of best coding C# standards when implementing my own push notification service. You can implement LINQ, Tasks async etc. I tested this dotAPNS library and it works perfectly fine. For Android you can still use PushSharp.

Before you implement the dotAPNSService helper class, get the following from your Apple developer account. The ApnsJwtOptions values should be:

BundleId - your app’s bundle ID. Should not include specific topics (i.e. com.myapp but not com.myapp.voip).

CertFilePath - path to the .p8 certificate you have downloaded from the Developer Center.

KeyId - The 10-character Key ID you obtained from your developer account

TeamId - The 10-character Team ID you use for developing your company’s apps. Obtain this value from your developer account.

public class dotAPNSService : IDisposable
{
    public event EventHandler OnTokenExpiredHandler;
    private ApnsJwtOptions options = null;
    
    public dotAPNSService()
    {
        options = new ApnsJwtOptions()
        {
            BundleId = "com.xx.xxxx",
            CertFilePath = "../../certificate.p8",
            KeyId = "The_Key_Id",
            TeamId = "The_Team_Id"
        };
    }

    public void SendNotifications(String[] deviceTokens, String title, String body)
    {
        if (deviceTokens == null || deviceTokens.Length <= 0)
        {
            return;
        }

        if (String.IsNullOrEmpty(title))
        {
            return;
        }

        if (String.IsNullOrEmpty(body))
        {
            return;
        }

        // once you've gathered all the information needed and created an options instance, it's time to call
        var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);

        // start the process     
        foreach (String deviceToken in deviceTokens)
        {
            var push = new ApplePush(ApplePushType.Alert)
                .AddAlert(title, body)
                .AddToken(deviceToken);

            Send(apns, push, deviceToken);
        }
    }

    public void SendSilentNotifications(String[] deviceTokens)
    {
        try
        {
            if (deviceTokens == null || deviceTokens.Length <= 0)
            {
                return;
            }

            // once you've gathered all the information needed and created an options instance, it's time to call
            var apns = ApnsClient.CreateUsingJwt(new HttpClient(), options);

            // start the process               
            foreach (String deviceToken in deviceTokens)
            {
                var push = new ApplePush(ApplePushType.Background)
                    .AddContentAvailable()
                    .AddToken(deviceToken);

                Send(apns, push, deviceToken);
            }
        }
        finally
        {

        }
    }

    private void Send(ApnsClient apns, ApplePush push, String deviceToken)
    {
        try 
        {
            var response = apns.SendAsync(push);
            if (response.Result.Reason == ApnsResponseReason.Success)
            {
                // the notification has been sent!
            }
            else 
            {
                Boolean removeToken = false;
                switch (response.Result.Reason)
                {
                    case ApnsResponseReason.BadDeviceToken:
                        removeToken = true;
                        break;
                    case ApnsResponseReason.TooManyRequests:
                        break;
                }

                // remove the token from database?
                if (removeToken)
                    OnTokenExpired(new ExpiredTokenEventArgs(deviceToken));
            }
        }
        catch (TaskCanceledException)
        {
            // ERROR - HTTP request timed out, you can use the deviceToken to log the error
        }
        catch (HttpRequestException ex)
        {
            // ERROR - HTTP request failed, you can use the deviceToken to log the error
        }
    }

    protected virtual void OnTokenExpired(ExpiredTokenEventArgs args)
    {
        try
        {
            EventHandler handler = OnTokenExpiredHandler;
            if (handler != null)
            {                    
                ISynchronizeInvoke target = handler.Target as ISynchronizeInvoke;
                if (target != null && target.InvokeRequired)
                    target.Invoke(handler, new object[] { this, args });
                else
                    handler(this, args);
            }
        }
        catch (Exception ex)
        {
           
        }
    }
}

These are the namespaces of the dotAPNSService helper class:

using System;
using System.ComponentModel;
using System.Net.Http;
using System.Threading.Tasks;
using dotAPNS;

In order to use the dotAPNSService helper on your project just pull the tokens from the database and then pass them to it. For instance, to send silent notifications:

public void SendScheduledSilentNotifications()
{
    try
    {
        IList<User> users = _accountService.GetUsers(true);
        if (users != null && users.Count > 0)
        {
            List<String> deviceTokens = new List<String>();
            foreach (User user in users)
            {
                if (!String.IsNullOrEmpty(user.DeviceToken))
                    deviceTokens.Add(user.DeviceToken);
            }
    
            if (deviceTokens.Count > 0)
            {
                using (dotAPNSService service = new dotAPNSService())
                {
                    service.OnTokenExpiredHandler += new EventHandler(OnTokenExpired);
                    service.SendSilentNotifications(deviceTokens.ToArray());
                }
            }
        }
    }
    finally
    {
    
    }
}

To remove the expired tokens from the database you can use the following:

private void OnTokenExpired(object sender, EventArgs e)
{
    if (e == null)
        return;

    if (e.GetType() == typeof(ExpiredTokenEventArgs))
    {
        var args = (ExpiredTokenEventArgs)e;
        User user = _accountService.GetUserByDeviceToken(args.Token);
        if (user != null)
        {
            user.DeviceToken = String.Empty;
            Boolean success = !(_accountService.SaveUser(user) == null);
            if (success)
                // INFO - expired device token has been removed from database
            else
                // INFO - something went wrong
        }   
    }
}

You can download the source code from here: https://github.com/alexalok/dotAPNS

The API is now sending thousands of silent notifications at one time and there are no delays, crashes etc. Hope this code snippet helps and saves your time!