How to save two items which comes with pushNotification - ios

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)
}
}
}
}

Related

Is it possible to send update notification to app users iOS

I have an application which is already LIVE on app store. Is it possible to send update notification to all the users whenever the new update is available?
Code for App Version Comparision is:
func checkAppUpdateAvailability(onSuccess: #escaping (Bool) -> Void, onError: #escaping (Bool) -> Void) {
guard let info = Bundle.main.infoDictionary,
let curentVersion = info["CFBundleShortVersionString"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=com.facebook.app") else {
return onError(true)
}
do {
let data = try Data(contentsOf: url)
guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
return onError(true)
}
if let result = (json["results"] as? [Any])?.first as? [String: Any], let appStoreVersion = result["version"] as? String {
print("version in app store", appStoreVersion," current Version ",curentVersion);
let versionCompare = curentVersion.compare(appStoreVersion, options: .numeric)
if versionCompare == .orderedSame {
onSuccess(false)
} else if versionCompare == .orderedAscending {
onSuccess(true)
// 2.0.0 to 3.0.0 is ascending order, so ask user to update
}
}
} catch {
onError(true)
}
}
Now you can check App Update.
checkAppUpdateAvailability { (status) in
//When status == true show popup.
} onError: { (status) in
// Handle error
}

Displaying Viewcontroller based on Notification payload swift

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}

swift ios how to access AnyHashable data in swift

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)
}
}
}

iOS Rich notifications not working correctly

I need to implement rich notification in current application with image.
I have implemented everything and its working fine. And receiving push notification, but getting only title and message but not image as attachments
Normal notifications without mutable-content work fine.
As far as I tested, there is no problem with certificate side.. I am using NotificationService extension to show media attachment
Any idea?
My payload looks like below:
[AnyHashable("title"): New trailer released- Hindi Movies, AnyHashable("message"): Kuchh Bheege Alfaaz - Official Hindi Trailer, AnyHashable("aps"): {
alert = "New trailer released- Hindi Movies";
badge = 2;
sound = default;
}, AnyHashable("trailer_img"): uploads/trailer_image/9703831856a7968b8b5cf6b10357dc2c1.jpg]
My NotificationService Class code like below:
class NotificationService: UNNotificationServiceExtension {
var contentHandler : ((UNNotificationContent) -> Void)?
var content : UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest,
withContentHandler contentHandler:
#escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
self.content = (request.content.mutableCopy()
as? UNMutableNotificationContent)
let userInfo : [AnyHashable: Any] = request.content.userInfo
print(userInfo)
if let bca = self.content {
func save(_ identifier: String,
data: Data, options: [AnyHashable: Any]?)
-> UNNotificationAttachment? {
let directory = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(ProcessInfo.processInfo.globallyUniqueString,
isDirectory: true)
do {
try FileManager.default.createDirectory(at: directory,
withIntermediateDirectories: true,
attributes: nil)
let fileURL = directory.appendingPathComponent(identifier)
try data.write(to: fileURL, options: [])
return try UNNotificationAttachment.init(identifier: identifier,
url: fileURL,
options: options)
} catch {
}
return nil
}
func exitGracefully(_ reason: String = "") {
let bca = request.content.mutableCopy()
as? UNMutableNotificationContent
bca!.title = reason
contentHandler(bca!)
}
let reachability = Reachability()!
reachability.whenReachable = { reachability in
DispatchQueue.main.async {
if reachability.isReachableViaWiFi {
guard let content = (request.content.mutableCopy()
as? UNMutableNotificationContent) else {
return exitGracefully()
}
let userInfo : [AnyHashable: Any] = request.content.userInfo
print(userInfo)
guard let attachmentURL = userInfo["trailer_img"]
as? String else {
return exitGracefully()
}
guard let imageData =
try? Data(contentsOf: URL(string: "http://techindiana.com/dev/truetrailer_app/" + attachmentURL)!)
else {
return exitGracefully()
}
guard let attachment =
save("image.png", data: imageData, options: nil)
else {
return exitGracefully()
}
content.attachments = [attachment]
contentHandler(content.copy() as! UNNotificationContent)
} else {
return exitGracefully()
}
}
}
reachability.whenUnreachable = { reachability in
DispatchQueue.main.async {
return exitGracefully()
}
}
do {
try reachability.startNotifier()
} catch {
return exitGracefully()
}
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bca = self.content {
contentHandler(bca)
}
}
}

How to create an iOS share extension for contacts

I am trying to create an iOS share extension to share contacts using the following code:
let contactType = "public.vcard"
override func viewDidLoad() {
let items = extensionContext?.inputItems
var itemProvider: NSItemProvider?
if items != nil && items!.isEmpty == false {
let item = items![0] as! NSExtensionItem
if let attachments = item.attachments {
if !attachments.isEmpty {
itemProvider = attachments[0] as? NSItemProvider
}
}
}
if itemProvider?.hasItemConformingToTypeIdentifier(contactType) == true {
itemProvider?.loadItemForTypeIdentifier(contactType, options: nil) { item, error in
if error == nil {
print("item: \(item)")
}
}
}
}
I am getting the following output:
item: Optional(<42454749 4e3a5643 4152440d 0a564552 53494f4e 3a332e30 0d0a5052 4f444944 3a2d2f2f 4170706c 6520496e 632e2f2f 6950686f 6e65204f 5320392e 322f2f45 4e0d0a4e 3a42656e 6e657474 3b436872 69733b3b 3b0d0a46 4e3a2043 68726973 20204265 6e6e6574 74200d0a 54454c3b 74797065 3d484f4d 453b7479 70653d56 4f494345 3b747970 653d7072 65663a28 36313529 c2a03432 352d3637 31390d0a 454e443a 56434152 440d0a>)
If i set a breakpoint I see that item has a type of NSSecureCoding. My question is how do I turn this into a CNContact?
The item you are getting is of type NSData. You have to transform it to String to get the vCard String:
if itemProvider?.hasItemConformingToTypeIdentifier(contactType) == true {
itemProvider?.loadItemForTypeIdentifier(contactType, options: nil) { item, error in
if error == nil {
if let data = item as? NSData, let vCardString = String(data: data, encoding: NSUTF8StringEncoding) {
print(vCardString)
}
}
}
}
When you are sharing any contacts, itemProvider contains vCard format of CNContact. To access that contact, you need to convert that data (type NSSecureCoding) to CNContact.
let uttypeContact = kUTTypeContact as String
if itemProvider.hasItemConformingToTypeIdentifier(uttypeContact) {
itemProvider.loadItemForTypeIdentifier(uttypeContact, options: nil, completionHandler: { (data, error) in
if error == nil {
do {
if let data = data as? NSData {
let contact = try CNContactVCardSerialization.contactsWithData(data)
print(contact)
}
} catch {}
}
})
}
[Update]
For more details https://forums.developer.apple.com/message/92115#92115
Updated answer of #joern to Swift 3:
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeContact as String) {
itemProvider.loadItem(forTypeIdentifier: kUTTypeContact as String, options: nil, completionHandler: { (contact, error) in
if let contactData = contact as? Data, let vCardString = String(data: contactData, encoding: .utf8) {
print(vCardString)
}
})
}
Also for an Action Extension you need to add the OperationQueue:
if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeContact as String) {
itemProvider.loadItem(forTypeIdentifier: kUTTypeContact as String, options: nil, completionHandler: { (contact, error) in
OperationQueue.main.addOperation {
if let contactData = contact as? Data, let vCardString = String(data: contactData, encoding: .utf8) {
print(vCardString)
}
}
})
}

Resources