来自 Amazon SNS 的 VoIP 推送
VoIP Push from Amazon SNS
我正在尝试从 Amazon SNS 发送 VoIP 推送,有一个 firebase 函数可以在收到 VoIP_token 时创建 ARN 并使 SNS 推送 VoIP 推送。现在我面临一个问题。
=> 推送来自 didReceiveIncomingPushWith 负载,但是在这个委托方法中,我正在尝试更新 firebase 值,正如您在代码中看到的那样,这也有效,android 在 5 分钟后发送推送来自已在 firebase 功能中配置的 SNS,但问题是有时推送有时不推送,在开发版本中它工作正常但是当我投入试飞时它经常工作
扩展 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", [=10=]) }.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()
}
您使用 Xcode 11 和 iOS13 吗?
Apple 对 VoIP 推送通知进行了重大更改。使用 VoIP 时,您需要向 CallKit 报告 "incoming call"。
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
我已经查看了您的代码,我认为您需要对 VOIP
进行一些说明。
当您收到 VOIP
推送时,您的应用最多激活 30 秒。
我注意到您在收到推入 iOS 设备时执行了一些繁重的操作。我认为这是您代码中的主要问题。
解决这个问题
降低 GPS 的精度级别。
删除或优化您的代码(只需将所需数据存储或更新到 firebase,避免无用的操作)
还有一件事,我想澄清一下推送有时来有时不来
- 如果您尝试向同一台设备发送多个 Push,那么一些
跳过推送。
- 转到亚马逊 SNS
- 点击手机:推送通知
- 点击创建平台应用,
- 用户 Apple 凭据"Used for development in sandbox" 如果您想在沙盒上测试 voip 通知,请选中此复选框。
- 推送证书类型 -- Voip 推送证书
- 上传证书 .P12 文件。
- 保存表格并复制 ARN
创建 Lambda 函数。下面的代码示例适用于沙箱和生产环境。
// 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."
});
}
}
};
我正在尝试从 Amazon SNS 发送 VoIP 推送,有一个 firebase 函数可以在收到 VoIP_token 时创建 ARN 并使 SNS 推送 VoIP 推送。现在我面临一个问题。
=> 推送来自 didReceiveIncomingPushWith 负载,但是在这个委托方法中,我正在尝试更新 firebase 值,正如您在代码中看到的那样,这也有效,android 在 5 分钟后发送推送来自已在 firebase 功能中配置的 SNS,但问题是有时推送有时不推送,在开发版本中它工作正常但是当我投入试飞时它经常工作
扩展 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", [=10=]) }.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()
}
您使用 Xcode 11 和 iOS13 吗? Apple 对 VoIP 推送通知进行了重大更改。使用 VoIP 时,您需要向 CallKit 报告 "incoming call"。
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
我已经查看了您的代码,我认为您需要对 VOIP
进行一些说明。
当您收到 VOIP
推送时,您的应用最多激活 30 秒。
我注意到您在收到推入 iOS 设备时执行了一些繁重的操作。我认为这是您代码中的主要问题。
解决这个问题
降低 GPS 的精度级别。
删除或优化您的代码(只需将所需数据存储或更新到 firebase,避免无用的操作)
还有一件事,我想澄清一下推送有时来有时不来
- 如果您尝试向同一台设备发送多个 Push,那么一些 跳过推送。
- 转到亚马逊 SNS
- 点击手机:推送通知
- 点击创建平台应用,
- 用户 Apple 凭据"Used for development in sandbox" 如果您想在沙盒上测试 voip 通知,请选中此复选框。
- 推送证书类型 -- Voip 推送证书
- 上传证书 .P12 文件。
- 保存表格并复制 ARN
创建 Lambda 函数。下面的代码示例适用于沙箱和生产环境。
// 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."
});
}
}
};