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}
Related
I have done push notification using FCM on the app side. When the user taps on the notification it navigates to specific viewController from the app side to custom SDK. I have tried many ways but viewController is not showing but all functionalities are loaded successfully.
1. First attempt: [Notification tap is detected and passed data from the app side to framework. Loaded particular viewController functionalities but viewController are not showing.]
When user tap notification from the app side, it will send data to the framework to show a particular viewController.
here is the code for the app side:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
if(application.applicationState == .active){
print("user tapped the notification bar when the app is in foreground")
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print("didReceive notification tapped",userInfo)
TaskName = userInfo[taskName] as? String ?? ""
print("TaskName::::", TaskName ?? "")
ProcessInstanceId = userInfo[processInstanceId] as? String ?? ""
print("ProcessInstanceId::::", ProcessInstanceId ?? "")
processDefinitionName = userInfo[ProcessDefinitionId] as? String ?? ""
print("processDefinitionName::::", processDefinitionName ?? "")
TaskID = userInfo[taskId] as? String ?? ""
print("taskID::::", TaskID ?? "")
FormKey = userInfo[formKey] as? String ?? ""
print("FormKey::::", FormKey ?? "")
Types = userInfo[type] as? String ?? ""
print("Type::::", Types ?? "")
Title = userInfo[title] as? String ?? ""
print("Title::::", Title ?? "")
Body = userInfo[body] as? String ?? ""
print("Body::::", Body ?? "")
CreatedDate = userInfo[created] as? String ?? ""
print("created:::", CreatedDate ?? "")
AFlowBuilder(self).callFormView(taskName: TaskName ?? "", processInstanceID: ProcessInstanceId ?? "", ProcessDefinitionName: processDefinitionName ?? "", taskId: TaskID ?? "", formKey: FormKey ?? "", title: Title ?? "", type: Types ?? "", body: Body ?? "", created: CreatedDate ?? "", formStatus: false)
}
completionHandler()
}
Here is the code from the framework side:
private var taskViewNew: UIViewController?
public func callFormView(taskName: String, processInstanceID: String, ProcessDefinitionName: String, taskId: String, formKey: String, title: String, type: String, body: String, created: String, formStatus: Bool){
let newNotification = makeSaveNotification(taskName: taskName, processInstanceID: processInstanceID, ProcessDefinitionName: ProcessDefinitionName, taskId: taskId, formKey: formKey, title: title, type: type, body: body, created: created, formStatus: formStatus)
let realm = try! Realm()
// let realmData = realm.objects(NotificationRealmModel.self).filter("name = \(userNameRealm ?? "")").first!
try! realm.write {
realm.add(newNotification)
print("ream notification saved path url:: call form view", realm.configuration.fileURL ?? "")
}
let controller = CreateCardViewController()
let str = formKey
let result = String(str.dropFirst(7))
print(result)
let s = String(result.dropLast(10))
print("newFormKey call form view", s )
let v = convap(text: s)
controller.processInstanceId = processInstanceID
controller.cardName = ""
controller.TaskIdValue = taskId
controller.formKey = v
controller.tabName = "NotificationTapped"
controller.fullFormKey = formKey
self.taskViewNew?.addChild(controller)
self.taskViewNew?.view.addSubview(controller.view)
controller.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// controller.didMove(toParent: taskViewNew)
taskViewNew?.present(controller, animated: true, completion: nil)
}
Second attempt: [Notification detected in the app side and passed value to viewController. And, Inside viewController framework method is called to load framework viewController but all functionality is loaded successfully but the view is not loaded]
Here is the code from the app side:
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: #escaping () -> Void) {
if(application.applicationState == .active){
print("user tapped the notification bar when the app is in foreground")
let userInfo = response.notification.request.content.userInfo
// Print message ID.
if let messageID = userInfo[gcmMessageIDKey] {
print("Message ID: \(messageID)")
}
// Print full message.
print("didReceive notification tapped",userInfo)
TaskName = userInfo[taskName] as? String ?? ""
print("TaskName::::", TaskName ?? "")
ProcessInstanceId = userInfo[processInstanceId] as? String ?? ""
print("ProcessInstanceId::::", ProcessInstanceId ?? "")
processDefinitionName = userInfo[ProcessDefinitionId] as? String ?? ""
print("processDefinitionName::::", processDefinitionName ?? "")
TaskID = userInfo[taskId] as? String ?? ""
print("taskID::::", TaskID ?? "")
FormKey = userInfo[formKey] as? String ?? ""
print("FormKey::::", FormKey ?? "")
Types = userInfo[type] as? String ?? ""
print("Type::::", Types ?? "")
Title = userInfo[title] as? String ?? ""
print("Title::::", Title ?? "")
Body = userInfo[body] as? String ?? ""
print("Body::::", Body ?? "")
CreatedDate = userInfo[created] as? String ?? ""
print("created:::", CreatedDate ?? "")
}
window = UIWindow(frame: UIScreen.main.bounds)
let homeViewController = ViewController()
homeViewController.Body = Body
homeViewController.CreatedDate = CreatedDate
homeViewController.FormKey = FormKey
homeViewController.processDefinitionName = processDefinitionName
homeViewController.ProcessInstanceId = ProcessInstanceId
homeViewController.TaskID = TaskID
homeViewController.Title = Title
homeViewController.Types = Types
homeViewController.view.backgroundColor = UIColor.red
window!.rootViewController = homeViewController
window!.makeKeyAndVisible()
completionHandler()
}
Here is the code viewController from the app side, it will load the framework viewController method.
Attempt 1:
override func viewDidAppear(_ animated: Bool) {
let controller = CreateCardViewController()
let str = FormKey
let result = String(str?.dropFirst(7) ?? "")
print(result)
let s = String(result.dropLast(10))
print("newFormKey call form view", s )
let v = convap(text: s)
controller.processInstanceId = ProcessInstanceId
controller.cardName = ""
controller.TaskIdValue = TaskID
controller.formKey = v
controller.tabName = "NotificationTapped"
print(controller.tabName)
controller.fullFormKey = FormKey
add(asChildViewController: controller)
}
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChild(viewController)
// Add Child View as Subview
view.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = view.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParent: self)
}
Attempt: 2
override func viewDidAppear(_ animated: Bool) {
print("Presenting next...")
let controller = CreateCardViewController()
let str = FormKey
let result = String(str?.dropFirst(7) ?? "")
print(result)
let s = String(result.dropLast(10))
print("newFormKey call form view", s )
let v = convap(text: s)
controller.processInstanceId = ProcessInstanceId
controller.cardName = ""
controller.TaskIdValue = TaskID
controller.formKey = v
controller.tabName = "NotificationTapped"
print(controller.tabName)
controller.fullFormKey = FormKey
present(controller, animated: true, completion: nil)
}
Any help much appreciated pls...
I have such task currently maybe it is a little bit simpler but this is my code:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {
if let navigationController = window?.rootViewController as? UINavigationController {
navigationController.popToRootViewController(animated: false)
if let someViewController = navigationController.storyboard?.instantiateViewController(withIdentifier: "SomeViewController") as? SomeViewController {
navigationController.pushViewController(someViewController, animated: true)
}
}
}
In my case, it works fine because I have UINavigationController as a root of my app, and I manage my navigation through it. This is important to be sure what you have on your stack of views right now and how to clear it.
Also, you can see that I use this line for instantiating my new ViewController
if let someViewController = navigationController.storyboard?.instantiateViewController(withIdentifier: "SomeViewController") as? SomeViewController
But this is not important, just be sure that your ViewController is correctly initiated.
Good luck.
I used the top view controller to present the desired view.
To fetch top view controller:
func topController() -> UIViewController {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
return topController
}
return (UIApplication.shared.keyWindow?.rootViewController)!
}
Then on receiving notification:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print(userInfo)
guard Utility.shared.userInfo != nil else { return }
let vc : EventDetailVC = EVENTSSTORYBOARD.viewController(id: "EventDetailVC") as! EventDetailVC
vc.eventID = event_id
vc.eventType = EventType.Invite
let nav = UINavigationController(rootViewController: vc)
nav.modalPresentationStyle = .fullScreen
self.topController().present(nav, animated: true, completion: nil)
}
Let me know if it works for you.
Use NotificationCenter create a custom NSNotification.Name and subscribe your viewcontroller to the created notification name using
NotificationCenter.default.addObserver(self, selector: #selector(openView), name: <<custom notification name>>, object: nil)
after that using the selector function you can pass all the parameters to the view controller
#objc func openView(notification: Notification){}
and in didFinishLaunchingWithOptions post to the above custom notification using this
let userInfo = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
if (userInfo != nil){
DispatchQueue.main.asyncAfter(deadline: .now() + 0.9) {
NotificationCenter.default.post(name: <<custom notification name>>, object: nil, userInfo: userInfo as! [AnyHashable : Any]?)
}
}
if you have multiple viewcontrollers that needs to be open you can create a base view controller class and put above code in the base class and extend the viewcontrollers by the base class
I have a bit of a lengthy question, So I apologize in advance I will try to illustrate this to the best of my abilities. I am trying to establish a notifications view controller that calls different types of data from Firebase and sets different notification types.
In the image above, this is how the cells should look when a user sends a notification to firebase. The user associated with that specific notification type as called and posted onto the screen.
In the firebase structure, We see that all of the information Stored is saved under the UID of the user in the first picture and is set under that specific users notification to show who is sending them a notification which is correct. These users names and images show perfectly as well as the image on the right.
The code I use to save this information is below,
fileprivate func saveSwipeToDataBase(didLike: Any) {
let swipeDate = Int(NSDate().timeIntervalSince1970)
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let cardUID = topCardView?.cardViewModel.uid else { return }
let documentData = ["workerId": uid,
"didLike": didLike,
"checked": 0,
"Swipe Date": swipeDate,
"type": SWIPE_INT_VALUE,
"posterId" : cardUID] as [String : Any]
self.postJobNotificationsIntoDatabseWithUID(uid: cardUID, values: documentData as [String : AnyObject])
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
And below is how I retrieve this information and store it onto the Notifications View controller.
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
})
}
}
}
Now that I got the correct way to setup up and show out of the way, I will show my enum and how I am distinguishing the different types of calls from firebase.
class userNotifications {
// MARK: - establish notificationTypes
enum NotificationType: Int, Printable {
case swipe
case accepted
case confirmed
case completed
case pay
var description: String {
switch self {
case .swipe: return " swiped on your Job "
case .accepted: return " accepted you to complete the job, "
case .confirmed: return " confirmed the job"
case .completed: return " completed the job"
case .pay: return " pay for completed"
}
}
init(index: Int) {
switch index {
case 0: self = .swipe
case 1: self = .accepted
case 2: self = .confirmed
case 3: self = .completed
case 4: self = .pay
default: self = .swipe
}
}
}
// MARK: - access firebaseData
var creationDate: Date!
var timeDate: Date!
var uid: String!
var fromId: String?
var workerId: String?
var user: User!
var poster: Poster!
var type: Int?
var notificationType: NotificationType!
var didCheck = false
init(user: User? = nil, poster: Poster? = nil, dictionary: Dictionary<String, AnyObject>) {
self.user = user
if let poster = poster {
self.poster = poster
}
if let swipeDate = dictionary["Swipe Date"] as? Double {
self.creationDate = Date(timeIntervalSince1970: swipeDate)
}
if let createDate = dictionary["creationDate"] as? Double {
self.creationDate = Date(timeIntervalSince1970: createDate)
}
if let swipeDate = dictionary["time&date"] as? Double {
self.timeDate = Date(timeIntervalSince1970: swipeDate)
}
if let type = dictionary["type"] as? Int {
self.notificationType = NotificationType(index: type)
}
if let uid = dictionary["uid"] as? String {
self.uid = uid
}
if let fromId = dictionary["fromId"] as? String {
self.fromId = fromId
}
if let workerId = dictionary["workerUID"] as? String {
self.workerId = workerId
}
if let checked = dictionary["checked"] as? Int {
if checked == 0 {
self.didCheck = false
} else {
self.didCheck = true
}
}
}
}
Above is the different types of notifications to be set.
Now, My issue is If I call a different notification type, such as .accepted, the information calls in a very different way.
The image above seems correct, However, the name and image are incorrect. it should be from the user ZacheryWilcox instead of Cjbwjdhbe. the user Cjbwjdhbe is the current user and the user who should be receing a notification from Zacherywilcox. not from itself.
In firebase, the information is saved as
the code I use to save this information is below
var workerUser: User? {
didSet {
let name = workerUser?.name
workerNameLabel.text = name
let workersUID = workerUser?.uid
workerNameLabel.text = name
guard let profileImage = workerUser?.profileImageUrl else { return }
workerImageView.loadImageUsingCacheWithUrlString(profileImage)
}
}
func saveUserData() {
let workUser = self.workerUser
guard let uid = Auth.auth().currentUser?.uid else { return }
let workerId = workUser?.uid
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
let user = User(dictionary: dictionary as [String : AnyObject])
workUser?.uid = snapshot.key
self.datePicker.datePickerMode = UIDatePicker.Mode.date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd yyyy/ hh:mm a"
let selectedDate = dateFormatter.string(from: self.datePicker.date)
let creationDate = Int(NSDate().timeIntervalSince1970)
print(selectedDate)
let docData: [String: Any] = [
"workerId": workerId!,
"time&date": selectedDate,
"posterId" : uid,
"creationDate": creationDate,
"location": user.address!,
"type": 1,
"jobPost": "someUIDString",
"checked": 0,
]
self.postJobNotificationsIntoDatabseWithUID(uid: workerId!, values: docData as [String : AnyObject])
}, withCancel: { (err) in
print("attempting to load information")
})
print("Finished saving user info")
self.dismiss(animated: true, completion: {
print("Dismissal complete")
})
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
When the type .accepted is being used to differentiate what notificationType is being called, the user who sent the notification is not being set correctly and I have no idea what is the reasoning behind this. The correct user that is sending this information over is Zacherywilcox, and that users image and name should be set to the user's notification screen. not the user Cjbe... I was wondering if anyone could help me fix these issues. Thank you in advance. I'm starting to think that the way I am saving the users information when accepting the user is incorrect.
When I am fetchingNotifications(), is it possible that since calling
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
has an effect on whats going on? if so, Is there a way to differentiate between what notificationType is being called and fetch what notifications has been called with their respective users?
Just update your code to:
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
let notificationId = snapshot.key
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let type = postingDictionary["type"] as? Int else { continue }
guard let uid = (type == userNotifications.NotificationType.accepted.rawValue) ? postingDictionary["fromId"] as? String : postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["fromId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
// NOTIFICATIONS_REF.child(currentUID).child(notificationId).child("checked").setValue(1)
})
}
}
}
This will solve your problem.
1)When notification comes,i need to save data from AnyHashable("data") "amount" and "inBalance"?
2)From server comes two pushes first push with "message",second "nil message body".There when nil message comes i need to silent it?
How i can save it?
I'm using xCode 10.2.1
my code for pushNotification.swift looks like:
enum PillikanRemoteNotification:Int {
case empty
case notification = 2
case news = 3
}
class PushManagerNotification {
func convert(with value: [AnyHashable: Any], and state: PushNotificationAction) {
guard let json = value as? [String: AnyObject], !json.isEmpty else { return }
guard let jsonString = value["data"] as? String, !jsonString.isEmpty else { return }
guard let jsonData = jsonString.data(using: .utf8) else { return }
guard let rawDictionary = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) else { return }
guard let dictionary = rawDictionary as? [String: AnyObject] else { return }
guard let actionType = dictionary["action"] as? String, let action = Int(actionType) else { return }
guard let notificationType = PillikanRemoteNotification(rawValue: action) else { return }
guard let messageBody = dictionary["msg"] as? [String: AnyObject] else { return }
if state == .show {
showBadges(dictionary: messageBody)
return
}
switch notificationType {
case .notification:
showNotification(dictionary: messageBody)
break
default:
break;
}
}
private func showBadges(dictionary: [String: AnyObject]) {
guard let badgeMessage = dictionary["badges"] as? [String: AnyObject] else { return }
guard let badges = Badges(JSON: badgeMessage) else { return }
UIApplication.shared.notificationBadgesForNotification = badges.notifications
UIApplication.shared.notificationBadgesForNews = badges.news
UIApplication.shared.applicationIconBadgeNumber = badges.news + badges.notifications
NotificationCenter.default.post(name: .badges, object: badges)
}
private func showNotification(dictionary: [String:AnyObject]) {
if let message = NotificationEntity(JSON: dictionary) {
NotificationCenter.default.post(name: .notification, object: message);
}
}
extension Notification.Name {
static let empty = Notification.Name("empty");
static let notification = Notification.Name("notification");
static let news = Notification.Name("news");
static let badges = Notification.Name("badges")
}
For Silent Notifications there are two criterions:
1.) The payload's aps dictionary must include the content-available key with a value of 1.
2.) The payload's aps dictionary must not contain the alert, sound, or badge keys.
example
{
"aps" : {
"content-available" : 1
},
"cusomtkey1" : "bar",
"cusomtkey2" : 42
}
inside the didReceive function use this :
let amount = userInfo["data"]["amount"]
let inBalance = userInfo["data"]["inBalance"]
I'm not sure if this is gonna work or not I didn't try it.. try this function for the notification properties:
#available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
let message = userInfo["message"] as! String
if message.isEmpty == true {
completionHandler([.alert, .badge])
}else {
completionHandler([.alert, .badge, .sound])
}
}
Here you can see, How I‘m taking token from AnyHashable and then take a specific value from it.
Try this code. It will help you out.
if(response.result.isSuccess){
let data = response.result.value
print(data!)
if (data?.contains("access_token"))!{
var dictonary:NSDictionary?
if let data = data!.data(using: String.Encoding.utf8) {
do {
dictonary = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject] as NSDictionary?
UserDefaults.standard.set(dictonary!["access_token"]!, forKey: "token")
}catch let error as NSError {
print(error)
}
}
}
}
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)
}
}
}
I'm creating a simple chat app, it has a loading screen with a segue to either the login screen if the user is not logged in or directly to his chats if he is. The chats are displayed in a UICollectionView. When I was first testing, I populated it with dummy data which I declared in the class itself, and everything worked fine. Now I am fetching the user's chats from an online database in the Loading Screen, and storing them in an array called user_chats which is declared globally.
I use the following code to populate the UICollectionView :
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// getUserChats()
return user_chats.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("chat_cell" , forIndexPath: indexPath) as! SingleChat
cell.chatName?.text = user_chats[indexPath.row].chat_partner!.name
cell.chatTextPreview?.text = user_chats[indexPath.row].chat_messages!.last!.text
let profile_pic_URL = NSURL(string : user_chats[indexPath.row].chat_partner!.profile_pic!)
downloadImage(profile_pic_URL!, imageView: cell.chatProfilePic)
cell.chatProfilePic.layer.cornerRadius = 26.5
cell.chatProfilePic.layer.masksToBounds = true
let dividerLineView: UIView = {
let view = UIView()
view.backgroundColor = UIColor(white: 0.5, alpha: 0.5)
return view
}()
dividerLineView.translatesAutoresizingMaskIntoConstraints = false
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-1-[v0]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
cell.addSubview(dividerLineView)
cell.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[v0(1)]|", options: NSLayoutFormatOptions(), metrics: nil, views: ["v0": dividerLineView]))
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("showChat", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showChat") {
let IndexPaths = self.collectionView!.indexPathsForSelectedItems()!
let IndexPath = IndexPaths[0] as NSIndexPath
let vc = segue.destinationViewController as! SingleChatFull
vc.title = user_chats[IndexPath.row].chat_partner!.name
}
}
DATA FETCH :
func getUserChats() {
let scriptUrl = "*****"
let userID = self.defaults.stringForKey("userId")
let params = "user_id=" + userID!
let myUrl = NSURL(string: scriptUrl);
let request: NSMutableURLRequest = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let data = params.dataUsingEncoding(NSUTF8StringEncoding)
request.timeoutInterval = 10
request.HTTPBody=data
request.HTTPShouldHandleCookies=false
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
for anItem in jsonArray as! [Dictionary<String, AnyObject>] {
let curr_chat = Chat()
if let chatId = anItem["chatId"] as? String {
curr_chat.id = chatId
}
let friend = Friend()
let user1id = anItem["user1_id"] as! String
let user2id = anItem["user2_id"] as! String
if (user1id == userID) {
if let user2id = anItem["user2_id"] as? String {
friend.id = user2id
}
if let user2name = anItem["user2_name"] as? String {
friend.name = user2name
}
if let user2profilepic = anItem["user2_profile_pic"] as? String {
friend.profile_pic = user2profilepic
}
}
else if (user2id == userID){
if let user1id = anItem["user1_id"] as? String {
friend.id = user1id
}
if let user1name = anItem["user1_name"] as? String {
friend.name = user1name
}
if let user1profilepic = anItem["user1_profile_pic"] as? String {
friend.profile_pic = user1profilepic
}
}
curr_chat.chat_partner = friend
var chat_messages = [Message]()
if let dataArray = anItem["message"] as? [String : AnyObject] {
for (_, messageDictionary) in dataArray {
if let onemessage = messageDictionary as? [String : AnyObject] { let curr_message = Message()
if let messageid = onemessage["message_id"] as? String {
curr_message.id = messageid
}
if let messagedate = onemessage["timestamp"] as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateFormatter.dateFromString(messagedate)
curr_message.date = date
}
if let messagesender = onemessage["sender"] as? String {
curr_message.sender = messagesender
}
if let messagetext = onemessage["text"] as? String {
curr_message.text = messagetext
}
chat_messages.append(curr_message)
}}
}
curr_chat.chat_messages = chat_messages
user_chats.append(curr_chat)
}
}
catch {
print("Error: \(error)")
}
}
// NSUserDefaults.standardUserDefaults().setObject(user_chats, forKey: "userChats")
}
else {
dispatch_async(dispatch_get_main_queue(), {
let uiAlert = UIAlertController(title: "No Internet Connection", message: "Please check your internet connection.", preferredStyle: UIAlertControllerStyle.Alert)
uiAlert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { action in
self.dismissViewControllerAnimated(true, completion:nil)
}))
self.presentViewController(uiAlert, animated: true, completion: nil)
})
}
} catch _ {
NSLog("error")
}
})
}
The problem is that the collection view is always empty now. I have done some debugging and put a breakpoint inside the first function, and I saw that this method is called when the Loading Screen is still displayed to the user and the chat screen hasn't even been loaded. My suspicion is that this is called before the data is fetched from the internet in the Loading Screen, and as a result the size of the user_chats array is 0. I am used to working with Android and ListView where the ListView are never populated until the parent view is displayed on screen, hence why I am confused. The method which fetches the data from the online database works fine as I have already extensively debugged it, so the problem isn't there.
The best option is to add a completionHandler to your function to be notified when the data is return and/or when the async function is finished executing. The code below is a truncated version of your getUserCharts function with a completionHandler, which returns a true or false when the data is load (You could modify this to return anything you wish). You can read more about closures/ completion handlers https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html or google.
function
func getUserChats(completionHandler: (loaded: Bool, dataNil: Bool) -> ()) -> (){
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{ (response: NSURLResponse?, data: NSData?, error: NSError?) -> Void in
do {
if (data != nil) {
do {
var dataString = String(data: data!, encoding: NSUTF8StringEncoding)
var delimiter = "]"
var token = dataString!.componentsSeparatedByString(delimiter)
dataString = token[0] + "]"
print(dataString)
let data_fixed = dataString!.dataUsingEncoding(NSUTF8StringEncoding)
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[])
// LOOP THROUGH JSON ARRAY AND FETCH VALUES
completionHandler(loaded: true, dataNil: false)
}
catch {
print("Error: \(error)")
}
}
}
else {
//Handle error or whatever you wish
completionHandler(loaded: true, dataNil: true)
}
} catch _ {
NSLog("error")
}
How to use it
override func viewDidLoad() {
getUserChats(){
status in
if status.loaded == true && status.dataNil == false{
self.collectionView?.reloadData()
}
}
}
It sounds like this is an async issue. I'm not sure how your project is setup but you need to call reloadData() on your collection view when the response is returned.
After you have received the data back from the server, and updated the data source for the collection view you need to refresh the collection view (Make sure you are on the main thread, since it is modifying the UI):
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
Edit:
Also, I'm not completely sure how you have your project setup, but you could create a delegate for your data fetch, so every time you get something back from the server it calls a delegate method that there are new messages. Your collection view controller would subscribe to that delegate, and every time the that method is called it would reload your collection view.
The Delegate:
protocol ChatsDelegate {
func didUpdateChats(chatsArray: NSArray)
}
In your Data Fetch:
user_chats.append(cur_chat)
self.delegate.didUpdateChats(user_chats)
In your collectionView controller:
class viewController: ChatsDelegate, ... {
...
func didUpdateChats(chatsArray: NSArray) {
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
}