I mat the problem with fcm push. I received push notification from our backend and I need change aps title.
For one type of payload notification service extension change title, for another not.
For these payload everything ok:
**POST:** https://fcm.googleapis.com/fcm/send
body:
{
"to" : "my fcm token",
"mutable_content": true,
"notification": {
"title": "{ some code } камера не в сети",
"body": "какое то тело"
},
"data": {
"type": 18990
}
}
Result:
UserInfo:
**type = 18990,**
gcm.message_id = 1669107644621636,
aps = {
alert = {
body = "\U043a\U0430\U043a\U043e\U0435 \U0442\U043e \U0442\U0435\U043b\U043e";
title = "{ some code } \U043a\U0430\U043c\U0435\U0440\U0430 \U043d\U0435 \U0432 \U0441\U0435\U0442\U0438";
};
"mutable-content" = 1;
},
google.c.fid = anyKey,
google.c.sender.id = 0000,
google.c.a.e = 1
But with these I had the problem:
**POST:** https://fcm.googleapis.com/fcm/send
body:
{
"to" : "my fcm token",
"mutable_content": true,
"notification": {
"title": "{ some code } камера не в сети",
"body": "какое то тело"
},
"data": {
"userAgreementId": 18990
}
}
Result:
UserInfo:
**userAgreementId = 18990**,
gcm.message_id = 1669107519431159,
aps = {
alert = {
body = "\U043a\U0430\U043a\U043e\U0435 \U0442\U043e \U0442\U0435\U043b\U043e";
title = "{ some code } \U043a\U0430\U043c\U0435\U0440\U0430 \U043d\U0435 \U0432 \U0441\U0435\U0442\U0438";
};
"mutable-content" = 1;
},
google.c.fid = anyKey,
google.c.sender.id = 0000,
google.c.a.e = 1
title change I did as mentioned in apple documentation (https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications)
I override both:
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
let userInfo = bestAttemptContent?.userInfo as? [String: Any]
if let bestAttemptContent = bestAttemptContent {
var title = bestAttemptContent.title
if let title_ = ((userInfo?["aps"] as? [String: Any])?["alert"] as? [String: Any])?["title"] as? String {
title = title_
}
title = title.lowercased().contains("камера не в сети") ? "Камера не в сети" : title
bestAttemptContent.title = title
contentHandler(bestAttemptContent)
}
}
and
override func serviceExtensionTimeWillExpire() {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
let userInfo = bestAttemptContent.userInfo as? [String: Any]
var title = bestAttemptContent.title
if let title_ = ((userInfo?["aps"] as? [String: Any])?["alert"] as? [String: Any])?["title"] as? String {
title = title_
}
title = title.lowercased().contains("камера не в сети") ? "Камера не в сети" : title
bestAttemptContent.title = title
contentHandler(bestAttemptContent)
}
}
Related
I created a small test project, enabled push notifications and remote notifications and added the template NotificationServiceExtension. When the notification is shown, the title isn't modified as the template would suggest, nor can I attach the debugger to the extension on the simulator.
The notification content has the mutable-content flag set properly, so I would expect it to be called.
{
"aps": {
"alert": {
"title": "Notification Title",
"body": "Notification Body"
},
"badge": 0,
"mutable-content": 1,
"sound": "default",
"category": "richMessage"
}
}
The code I'm using to build the notification:
guard let jsonResult = convertToDictionary(text: incomingData),
let apsData = jsonResult["aps"] as? [String: Any],
let alert = apsData["alert"] as? [String: String],
let title = alert["title"],
let body = alert["body"],
let category = apsData["category"] as? String else { return }
let localNotification = UNMutableNotificationContent()
localNotification.title = title
localNotification.body = body
localNotification.categoryIdentifier = category
let identifier = "SurveyIdentifier"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
let request = UNNotificationRequest(identifier: identifier, content: localNotification, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
I am getting a push notification and displaying a viewcontroller based on the content of the notification.When the notification is triggered manually from postman, I get the following userInfo
[AnyHashable("google.c.a.e"): 1, AnyHashable("gcm.notification.type"): new_property, AnyHashable("gcm.notification.meta"): {"property_id":"3"}, AnyHashable("gcm.message_id"): 1570614460795417, AnyHashable("aps"): {
alert = {
body = "A new property has been published is in your area";
title = "New Property";
};
}]
// CASTING AS [String: Any]
["google.c.a.e": 1, "gcm.notification.meta": {"property_id":"3"}, "gcm.notification.type": new_property, "aps": {
alert = {
body = "A new property has been published is in your area";
title = "New Property";
};
}, "gcm.message_id": 1570614460795417]
This in turn works and displays the required screen. But when ever a user action triggers the same notification, I get this userInfo
[AnyHashable("gcm.notification.meta"): {"property_id":46}, AnyHashable("gcm.notification.type"): new_property, AnyHashable("body"): A new property has just been listed in your area by Ebenezer Kilani, AnyHashable("title"): New property Listing, AnyHashable("type"): new_property, AnyHashable("meta"): {"property_id":46}, AnyHashable("aps"): {
alert = {
body = "A new property has just been listed in your area by Ebenezer Kilani";
title = "New property Listing";
};
}, AnyHashable("gcm.message_id"): 1570550088749025, AnyHashable("google.c.a.e"): 1]
// CASTING AS [String: Any]
["meta": {"property_id":46}, "type": new_property, "title": New property Listing, "gcm.message_id": 1570550088749025, "gcm.notification.meta": {"property_id":46}, "body": A new property has just been listed in your area by Ebenezer Kilani, "aps": {
alert = {
body = "A new property has just been listed in your area by Ebenezer Kilani";
title = "New property Listing";
};
}, "google.c.a.e": 1, "gcm.notification.type": new_property]
the second payload just opens the application but never goes into the view controller I need it to. That is, it opens the home page only.
How I am getting the userInfo
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
if let usrInfo = userInfo as? [String: Any] {
extractUserInfo(userInfo: usrInfo, completion: completionHandler)
}
completionHandler()
}
func extractUserInfo(userInfo: [String: Any], completion: #escaping () -> Void) {
if let type = userInfo["gcm.notification.type"] as? String {
Storage.instance.savePush(type)
if type == "new_property" {
if let meta = userInfo["gcm.notification.meta"] as? String {
if let data = meta.data(using: .utf8) {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] {
guard let propertyId = json["property_id"] as? String, let id = Int(propertyId) else {return}
NotificationEvent.isFromNotification.accept(true)
NotificationEvent.id.accept(id)
} else {
print("JSON is invalid")
}
} catch {
print("Exception converting: \(error)")
}
}
}
}
}
}
how I handle the push to display required Viewcontroller
class Remote {
var navigationController: UINavigationController?
let disposeBag = DisposeBag()
#discardableResult
init(navigationController: UINavigationController?) {
self.navigationController = navigationController
rxEvent()
}
func rxEvent() {
NotificationEvent.isFromNotification.asObservable().subscribe(onNext: { bool in
print("Exception converting: bool \(bool)")
if bool {
NotificationEvent.id.asObservable()
.delay(1, scheduler: MainScheduler.instance)
.subscribe(onNext: { int in
if int > 0 {
if Storage.instance.getPush() == "new_property" {
self.presentPropertyDetailVC(int)
} else if Storage.instance.getPush() == "new_rating" || Storage.instance.getPush() == "coverage_area" {
print(int)
}
}
}).disposed(by: self.disposeBag)
NotificationEvent.isFromNotification.accept(false)
}
}).disposed(by: disposeBag)
}
func presentPropertyDetailVC(_ uid: Int) {
// navigationVC.modalPresentationStyle = .formSheet
let controller = PropertyDetailVC()
controller.propertyId = uid
print("Exception converting: 11 \(controller)")
navigationController?.pushViewController(controller, animated: true)
}
}
then I instantiate Remote in my home VC
Any reason why the first payload work and the second does not and anything I could do to correct that.
This line
guard let propertyId = json["property_id"] as? String, let id = Int(propertyId) else {return}
is getting to the "return" because on the second meta 46 is an int not a string (note that it have not quotes. To fix it you can try
guard let propertyId = json["property_id"] as? String ?? String(json["property_id"] as? Int ?? 0), let id = Int(propertyId) else {return}
When my app is killed I am not receiving push notification, but is working fine with the background. After receiving notification I am generating a local notification by getting data from my realm DB. In payload content available key is 1. Please suggest me where I am going wrong, with getting fcm notification or running in the background.
here is my code -
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
let state: UIApplicationState = UIApplication.shared.applicationState
print(userInfo)
if state == .active{
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
} else if state == .inactive {
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
} else if state == .background {
let messageBody = userInfo["aps"] as! Dictionary<String,Any>
print("############# got notification")
if let message = messageBody["message"] {
let messageDict = DCUtilityClass.convertToDictionary(text: message as! String)
let _ = DCPushNotificationAPI.getNotificationResult(info: (messageDict)!)
}
}
completionHandler(UIBackgroundFetchResult.noData)
//receiveMessageMethod(recieveMessage: messageBody as! Dictionary<String, Any>)
}
and
class func triggerLocalPushNotification(title:String, body:Dictionary<String,Any>) {
//creating the notification content
let content = UNMutableNotificationContent()
//adding title, subtitle, body and badge
let messageDictModel = DCChatListHistoryModel.init().initWithUserChatDictionary(userDictionary: body)
if messageDictModel.mIsGroup{
if messageDictModel.mGroupDetailsModelArray.count == 0 {
let groupData = DCDBDataUtilityClass.getGroupInfoByConversationId(id: messageDictModel.m_id)
content.title = (groupData?.mGroupsModelArray.first?.mGroup_name)!
content.body = messageDictModel.mlatest_message
} else {
content.title = (messageDictModel.mGroupDetailsModelArray.first?.mGroup_name)!
content.body = messageDictModel.mlatest_message
}
} else {
var messageSender = ""
if messageDictModel.mMessageFrom == UserDefaults.standard.value(forKey: "user_id") as! String {
messageSender = messageDictModel.mMessage_to
} else {
messageSender = messageDictModel.mMessageFrom
}
let name = DCDBDataUtilityClass.getInfoById(id: messageSender)
content.title = (name?.mFull_name)!
content.body = messageDictModel.mlatest_message
}
//getting the notification trigger
//it will be called after 5 seconds
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.2, repeats: false)
//getting the notification request
let request = UNNotificationRequest(identifier: "iOSNotification", content: content, trigger: trigger)
//adding the notification to notification center
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
I have two data's
1. From json
2. From json.file
Tha data format is below:-
{
"data": [
{
"question": "Gender",
"options": [
"Male",
"Female"
],
"button_type": "2"
},
{
"question": "How old are you",
"options": [
"Under 18",
"Age 18 to 24",
"Age 25 to 40",
"Age 41 to 60",
"Above 60"
],
"button_type": "2"
},
{
"button_type": "2",
"question": "I am filling the Questionnaire for?",
"options": [
"Myself",
"Mychild",
"Partner",
"Others"
]
}
]
}
This is my model:-
enum NHAnswerType:Int
{
case NHAnswerCheckboxButton = 1
case NHAnswerRadioButton
case NHAnswerSmileyButton
case NHAnswerStarRatingButton
case NHAnswerTextButton
}
class NH_QuestionListModel: NSObject {
var dataListArray33:[NH_OptionsModel] = []
var id:Int!
var question:String!
var buttontype:String!
var options:[String]?
var v:String?
var answerType:NHAnswerType?
var optionsModelArray:[NH_OptionsModel] = []
init(dictionary :JSONDictionary) {
guard let question = dictionary["question"] as? String,
let typebutton = dictionary["button_type"] as? String,
let id = dictionary["id"] as? Int
else {
return
}
// (myString as NSString).integerValue
self.answerType = NHAnswerType(rawValue: Int(typebutton)!)
if let options = dictionary["options"] as? [String]{
print(options)
for values in options{
print(values)
let optionmodel = NH_OptionsModel(values: values)
self.optionsModelArray.append(optionmodel)
}
}
self.buttontype = typebutton
self.question = question
self.id = id
}
}
optionmodel:-
class NH_OptionsModel: NSObject {
var isSelected:Bool? = false
var textIndexPath :IndexPath?
var dummyisSelected:Bool? = false
var v:String?
var values:String?
init(values:String) {
self.values = values
print( self.values)
}
}
Viewmodel in questionviewmodel:-
func loadData(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromWebserviceData { (newDataSourceModel) in
if(newDataSourceModel != nil)
{
self.datasourceModel = newDataSourceModel!
completion(true)
}
else{
completion(false)
}
}
}
func loadFromWebserviceData(completion :#escaping (NH_QuestionDataSourceModel?) -> ()){
//with using Alamofire ..............
// http://localhost/json_data/vendorlist.php
Alamofire.request("http://www.example.com").validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON{ response in
let status = response.response?.statusCode
print("STATUS \(status)")
print(response)
switch response.result{
case .success(let data):
print("success",data)
let result = response.result
print(result)
if let wholedata = result.value as? [String:Any]{
print(wholedata)
// let data2 = wholedata["data"] as? [String:Any]
self.datasection1 = wholedata
if let data1 = wholedata["data"] as? Array<[String:Any]>{
print(data)
print(response)
for question in data1 {
let typebutton = question["button_type"] as? String
print(typebutton)
self.type = typebutton
let options = question["options"] as! [String]
// self.dataListArray1 = [options]
self.tableArray.append(options)
// self.savedataforoptions(completion: <#T##(NH_OptionslistDataSourceModel?) -> ()#>)
self.no = options.count
}
print(self.tableArray)
let newDataSource:NH_QuestionDataSourceModel = NH_QuestionDataSourceModel(array: data1)
completion(newDataSource)
}
}
case .failure(let encodingError ):
print(encodingError)
// if response.response?.statusCode == 404{
print(encodingError.localizedDescription)
completion(nil)
}
}}
dummy data viewmodel:-
func loadFromDummyData(completion :#escaping (NH_DummyDataSourceModel?) -> ()){
if let path = Bundle.main.path(forResource: "jsonData", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
do {
let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
// self.datasection2 = jsonResult as! [String : Any]
let people1 = jsonResult["data"] as? [String:Any]
self.datasection2 = jsonResult as! [String : Any]
if let people = jsonResult["data"] as? Array<[String:Any]> {
// self.dict = people
for person in people {
let options = person["options"] as! [String]
self.tableArray.append(options)
let name = person ["question"] as! String
self.tableArray.append(options)
}
let newDataSource:NH_DummyDataSourceModel = NH_DummyDataSourceModel(array: people)
completion(newDataSource)
}
} catch {}
} catch {}
}
}
func loadData1(completion :#escaping (_ isSucess:Bool) -> ()){
loadFromDummyData{ (newDataSourceModel) in
if(newDataSourceModel != nil)
{
self.datasourceModel = newDataSourceModel!
completion(true)
}
else{
completion(false)
}
}
}
finally in the viewcontroller:-
in viewDidLoad:-
questionViewModel.loadData { (isSuccess) in
if(isSuccess == true)
{
let sec = self.questionViewModel.numberOfSections()
for _ in 0..<sec
{
self.questionViewModel.answers1.add("")
self.questionViewModel.questions1.add("")
self.questionViewModel.questionlist1.add("")
}
self.item1 = [self.questionViewModel.datasection1]
self.activityindicator.stopAnimating()
self.activityindicator.isHidden = true
self.tableview.refreshControl = refreshControl
self.tableview .allowsMultipleSelection = false
self.tableview.reloadData()
self.dummyDataViewModel.loadData1{ (isSuccess) in
if(isSuccess == true)
{
}
else{
self.viewDidLoad()
}
}
}
else{
self.activityindicator.stopAnimating()
self.activityindicator.isHidden = true
let controller = UIAlertController(title: "No Internet Detected", message: "This app requires an Internet connection", preferredStyle: .alert)
// Create the actions
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
NSLog("OK Pressed")
self.viewDidLoad()
}
controller.addAction(okAction)
self.present(controller, animated: true, completion: nil)
}
self.sections = [Category(name:"A",items:self.item1),
Category(name:"B",items:self.item2),
]
print(self.sections)
self.tableview.reloadData()
}
This is the format from json.file and also from the api.
I have used tableview.
So i need to list the header title from the key "question"
And the cell for row should display from the option keys.
So how to add this two data from the Json and Json.file?
You can create your models which represents your model. Preferably like
struct ResponseModel: Codable {
var data: [Question]?
}
struct QuestionModel{
var question: String?
var options: [String]?
}
Then after your Ajax post use
let responseData = try JSONDecoder().decode(AjaxResponse<ResponseModel>.self, from: data!)
Now you have your data.
You can then do in your UITableViewDeleates
//number of sections
resposnseData.data.count
//number of rows in section
responseData.data[section].options.count
//cell for index modify your cell using
response.data[indexPath.section].options[indexPath.row]
Hope it helps.
How to access any AnyHashable data in swift i have my data below , below is the log when i click the notification . When i click notif and when the data logs i want to print or get it so that i can load it on the view . it is already working and it log when i click the notif what i want is how to get it to load in the view.
what i have done
guard
let aps = userInfo[AnyHashable("aps")] as? NSDictionary,
let alert = aps["alert"] as? NSDictionary,
let body = alert["body"] as? String,
let title = alert["title"] as? String
else {
return
}
print("Title: \(title) \nBody:\(body)")
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
data
[AnyHashable("aps"): {
alert = {
body = "yuuu - 5.00";
title = "New Chore!";
};
"content-available" = 1;
}, AnyHashable("data"): {
chore = {
desc = huu;
name = yuuu;
pk = 125;
reward = "5.00";
sched = "2018-04-12T09:52:13+08:00";
};
"notification_id" = 16;
pusher = {
publishId = "pubid-01ff965a-20af-4a58-9901-89782043d832";
};
}]
You can Possibly Try:
Update for Swift 5+:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
guard let arrAPS = userInfo["aps"] as? [String: Any] else { return }
if application.applicationState == .active{
guard let arrAlert = arrAPS["alert"] as? [String:Any] else { return }
let strTitle:String = arrAlert["title"] as? String ?? ""
let strBody:String = arrAlert["body"] as? String ?? ""
let alert = UIAlertController(title: strTitle, message: strBody, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) { action in
print("OK Action")
})
self.window?.rootViewController?.present(alert, animated: true)
} else {
guard let arrNotification = arrAPS["notification"] as? [String:Any] else { return }
guard let arrAlert = arrNotification["alert"] as? [String:Any] else { return }
let strTitle:String = arrAlert["title"] as? String ?? ""
print("Title --", strTitle)
let strBody:String = arrAlert["body"] as? String ?? ""
print("Body --", strBody)
}
}
Swift 2+ :
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
guard let dictAPS = userInfo["aps"] as? NSDictionary else { return }
if application.applicationState == .active{
let title = dictAPS.value(forKeyPath: "alert.title")
let body = dictAPS.value(forKeyPath: "alert.body")
let alert = UIAlertController(title: "\(title!)", message: "\(String(describing: body))", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) { action in
})
self.window?.rootViewController?.present(alert, animated: true)
}else{
guard let dictNoti = dictAPS.value(forKey: "notification") as? NSDictionary else { return }
let title = dictNoti.value(forKeyPath: "alert.title")
print(title)
let body = dictNoti.value(forKeyPath: "alert.body")
print(body)
}
}
anwer : on how to load notif data when notif is tap
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo["gcmMessageIDKey"] {
print("Message ID: \(messageID)")
}
switch response.actionIdentifier {
case "action1":
print("Action First Tapped")
case "action2":
print("Action Second Tapped")
default:
break
}
// Print full message.
print(userInfo)
Messaging.messaging().appDidReceiveMessage(userInfo)
completionHandler()
}
APS data can be checked as below. You can check each keys as below to avoid crashing app if particular key is not available in notification data and handle accordingly.
if (userInfo["aps"] as? [String:Any]) != nil {
if let data = userInfo["data"] as? String{
if let desc = userInfo["desc"] as? String {
//Access variable desc here...
print(desc)
}
}
}