VoIP Push from Amazon SNS

Viewed 1029

I am trying to send VoIP push from Amazon SNS, there is firebase function which create ARN whenever VoIP_token is received and it makes SNS to push VoIP push. now there is one problem I am facing.

=> push is coming inside didReceiveIncomingPushWith payload, but inside this delegate method, I am trying to update firebase value as you can see in code, this is also working, android is sending push after 5 mins from SNS which has been configure in firebase function but the problem is push sometimes coming and sometimes not, in development version it's working fine but when I put in on test flight it works oftenly

extension AppDelegate : PKPushRegistryDelegate {

@available(iOS 8.0, *)
func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
    if #available(iOS 10.0, *) {
        Timer.scheduledTimer(withTimeInterval: 30, repeats: true) {_ in
            self.myMethod()
        }
    }
    else {
        Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(self.myMethod), userInfo: nil, repeats: true)
    }

    let token = credentials.token.map { String(format: "%02x", $0) }.joined()

    voipTokenLogin = token
    voip_Token = token
    UserDefaults.standard.set(voipTokenLogin, forKey: "voip_Push_From_Amazon_SNS")
    var objRef : DatabaseReference!
    _ = String()

    if MyCurrentUSERID != "" && isUserLoggedIn {
        objRef = Database.database().reference().child("user").child(MyCurrentUSERID)
        let battery = UIDevice.current.batteryLevel * 100
        let Intbattery = Int(battery)
        let dict = [
            "isOnline" : true,
            "isGps" : true,
            "voip_token": voip_Token,
            "Battery" : Intbattery,
            "lastSeen" : Int(Date().millisecondsSince1970),
            "timeStamp" : Date().millisecondsSince1970
            ] as [String : Any]
        objRef.updateChildValues(dict)
    }
    else{
    }
}

@available(iOS 8.0, *)

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void){
    var arrTemp = [AnyHashable: Any]()
    arrTemp = payload.dictionaryPayload
    let _ : Dictionary <String, AnyObject> = arrTemp["aps"] as! Dictionary<String, AnyObject>
    if isUserHasLoggedInWithApp // Check this flag then only proceed
    {
        if UIApplication.shared.applicationState == UIApplicationState.background || UIApplication.shared.applicationState == UIApplicationState.inactive
        {
            if checkForIncomingCall
            {
                Locator.currentPosition(accuracy: .city, onSuccess: { (loc) -> (Void) in
                    isUpdatedUsingSilentPush = true

                    if self.isUserLoggedIn{
                        if !isUploadingRecentLocation {
                            if !self.sendOneUpdate{
                    self.sendLocationToFirebase(manager: Locator, Location: loc)
                            }
                        }
                    }

                }) { (err, loct) -> (Void) in
                    print(err)
                    print(loct as Any)
                }
                //
                var objRef : DatabaseReference!
                if MyCurrentUSERID != "" {
                    objRef = Database.database().reference().child("user").child(MyCurrentUSERID)
                    let dict = [
                        "timeStamp" : Date().millisecondsSince1970,
                        "isOnline" : true,
                        "isGps" : true,
                        "lastSeen" : Int(Date().millisecondsSince1970)
                        ] as [String : Any]

                    objRef.updateChildValues(dict)

                      var strTitle : String = dict["alertTitle"] as? String ?? "\(MyCurrentUSERID)"
                    let strBody : String = dict["alertBody"] as? String ?? "push arrived successfully"
                    strTitle = strTitle + "\n" + strBody
                    self.myMethod()
                    let notificationIncomingCall = UILocalNotification()
                    notificationIncomingCall.fireDate = Date(timeIntervalSinceNow: 1)
                   notificationIncomingCall.alertBody =  strTitle
                    notificationIncomingCall.alertAction = "Open"
                    notificationIncomingCall.soundName = "SoundFile.mp3"
                    notificationIncomingCall.category = dict["category"] as? String ?? ""
                    //"As per payload you receive"
                    notificationIncomingCall.userInfo = ["key1": "Value1"  ,"key2": "Value2" ]
                UIApplication.shared.scheduleLocalNotification(notificationIncomingCall)
                }

            }
            else
            {
                //  something else
            }

        }
    }
    completion()
}
3 Answers

I have reviewed your code and I think you need some clarification of VOIP.

While you're received VOIP Push, Your app became active for a maximum 30 seconds.

I have noticed that you're performed some heavy operation while received the Push into iOS Device. I think it the main issue in your code.

To resolved this issue

Reduce the accuracy level of GPS.

Remove or optimize your code(Just store or update only required data to firebase avoid unused operation)

One more thing, I want to clarify for Push sometimes coming and sometimes not

  • If you tried to send multiple Push to the same device then some of the push is skipped.

Do you use Xcode 11 and iOS13? Apple did major changes to VoIP push notifications. When using VoIP you're expected to report an "incoming call" to CallKit.

On iOS 13.0 and later, if you fail to report a call to CallKit, the system will terminate your app. Repeatedly failing to report calls may cause the system to stop delivering any more VoIP push notifications to your app. If you want to initiate a VoIP call without using CallKit, register for push notifications using the UserNotifications framework instead of PushKit.

https://developer.apple.com/documentation/pushkit/pkpushregistrydelegate/2875784-pushregistry

  1. Go to Amazon SNS
  2. Click on Mobile: Push notifications
  3. Click on Create platform Application,
  4. User Apple Credentials "Used for development in sandbox" check this checkbox if you want to test voip notification on sandbox.
  5. Push certificate type -- Voip Push certificate
  6. Upload Certificate .P12 files.
  7. Save Form and copy ARN

https://docs.aws.amazon.com/sns/latest/dg/sns-send-custom-platform-specific-payloads-mobile-devices.html

Create Lambda function. Code example below for both sandbox and production.

// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'us-east-1'});

var amazonSNS = new AWS.SNS();

const isset = (s) => {
    return typeof s !== typeof undefined ? true : false;
};

exports.handler =  (event, context, callback) => {
    let data = event['body-json'];
    if (isset(data)) {
        let getAction = data['action'];
        let userToken = data['token'];
        let webPayload = data['payload'];

        if (isset(getAction) && getAction == 'APNS_VOIP') {
            amazonSNS.createPlatformEndpoint({
                PlatformApplicationArn: 'YOUR APPLICATION ARN',
                Token: userToken
            }, function (err, data1) {
                if (err) {
                    //handle error
                    console.log(err)
                } else {
                    //Publish notification
                    var endpointArn = data1.EndpointArn;
                    console.log("endpointArn",endpointArn);
                    var payload = {
                        default: 'Silent voip push notification',
                        APNS_VOIP: {
                            //aps:webPayload
                            "aps" : {
                                "alert" : {
                                    "title" : "Call Request"
                                },
                                "data":webPayload,
                                "content-available":1,
                                "category":"GENERAL"
                            }
                        }
                    };
                    payload.APNS_VOIP = JSON.stringify(payload.APNS_VOIP);
                    payload = JSON.stringify(payload);
                    amazonSNS.publish({
                        MessageStructure: 'json',
                        Message: payload,
                        MessageAttributes:{
                            "AWS.SNS.MOBILE.APNS.PRIORITY":{"DataType":"String","StringValue":"10"}, 
                            "AWS.SNS.MOBILE.APNS.PUSH_TYPE":{"DataType":"String","StringValue":"voip"} 
                        },
                        TargetArn: endpointArn
                    }, function (err, data2) {
                        if (err) {
                            //handle error
                            console.log(err)
                        } else {
                            var params = {
                                EndpointArn: endpointArn /* required */
                            };
                            //Deleting platform endpoint after sending a voipNotification; 
                            amazonSNS.deleteEndpoint(params, function (err, data3) {
                                if (err) {
                                    //handle error
                                    //console.log(err);
                                    callback(null, {
                                        "status": "error",
                                        "data": [],
                                        "message": []
                                    });
                                } else {
                                    //code success
                                    console.log("delete",data3);
                                    callback(null, {
                                        "status": "Success",
                                        "data": data3,
                                        "message":"notification sent successfully"
                                    });
                                }
                            });
                        }
                    });
                }
            });
        }
        else if (isset(getAction) && getAction == 'APNS_VOIP_SANDBOX') {
            amazonSNS.createPlatformEndpoint({
                PlatformApplicationArn: 'YOUR APPLICATION ARN',
                Token: userToken
            }, function (err, data1) {
                if (err) {
                    //handle error
                    console.log(err)
                } else {
                    //Publish notification
                    var endpointArn = data1.EndpointArn;
                    console.log("endpointArn",endpointArn);
                    var payload = {
                            default: 'Silent voip push notification',
                            APNS_VOIP_SANDBOX: {
                        "aps" : {
                            "alert" : {
                                "title" : "Call Request"
                            },
                            "data":webPayload,
                            "content-available":1,
                            "category":"GENERAL"
                        }
                    }
                        };
                    payload.APNS_VOIP_SANDBOX = JSON.stringify(payload.APNS_VOIP_SANDBOX);
                    payload = JSON.stringify(payload);
                    amazonSNS.publish({
                        MessageStructure: 'json',
                        Message: payload,
                        MessageAttributes:{
                            "AWS.SNS.MOBILE.APNS.PRIORITY":{"DataType":"String","StringValue":"10"}, 
                            "AWS.SNS.MOBILE.APNS.PUSH_TYPE":{"DataType":"String","StringValue":"voip"} 
                        },
                        TargetArn: endpointArn
                    }, function (err, data2) {
                        if (err) {
                            //handle error
                            console.log(err)
                        } else {
                            var params = {
                                EndpointArn: endpointArn /* required */
                            };
                            //Deleting platform endpoint after sending a voipNotification; Ref: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#deleteEndpoint-property
                            amazonSNS.deleteEndpoint(params, function (err, data3) {
                                if (err) {
                                    //handle error
                                    //console.log(err);
                                    callback(null, {
                                        "status": "error",
                                        "data": [],
                                        "message": []
                                    });
                                } else {
                                    //code success
                                    console.log("delete",data3);
                                    callback(null, {
                                        "status": "Success",
                                        "data": data3,
                                        "message":"notification sent successfully"
                                    });
                                }
                            });
                        }
                    });
                }
            });
        }
        else {
            callback(null, {
                "status": "error",
                "data": [],
                "message": "You have not posted valid data."
            });
        }
    }

};
Related