Remote Push notifications actions - swift2

I created a code that receive remote push norification, this code is worked ok. Now I need to add two "buttons" that swip left and do a action. I know that this below code is used to indetify the action
func application(application: UIApplication, handleActionWithIdentifier identifier: String?, forLocalNotification notification: UILocalNotification, completionHandler: () -> Void) {
if identifier == "optin1" {
//do something
}
else identifier == "option2" {
//do something
}
completionHandler()
}
But I dont knew how to create the buttons to swip left. How can I do it
This is my AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Override point for customization after application launch.
let types:UIUserNotificationType = [.Alert, .Sound, .Badge]
application.registerForRemoteNotifications()
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: types, categories: nil))
initLocationManager()
return true
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
//print(error)
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print(deviceToken)
}
func application(application: UIApplication, didReceiveRemoteNotificationuserInfo userInfo: [NSObject : AnyObject]) {
print(userInfo)
}

Update: new version Swift 4 compatible. This example was adapted from this amazing tutorial https://cocoacasts.com/actionable-notifications-with-the-user-notifications-framework
Configure User Notification Center
import UserNotifications
...
UNUserNotificationCenter.current().delegate = self
Define Actions
let actionReadLater = UNNotificationAction(identifier: Constants.Action.readLater,
title: "Read Later",
options: [])
Define Category
let tutorialCategory = UNNotificationCategory(identifier: Constants.Category.tutorial,
actions: [actionReadLater],
intentIdentifiers: [],
options: [])
Register Category
UNUserNotificationCenter.current().setNotificationCategories([tutorialCategory])
Schedule local notifications by instance from an IBAction
// Request Notification Settings
UNUserNotificationCenter.current().getNotificationSettings { (notificationSettings) in
switch notificationSettings.authorizationStatus {
case .notDetermined:
self.requestAuthorization(completionHandler: { (success) in
guard success else { return }
// Schedule Local Notification
self.scheduleLocalNotification()
})
case .authorized:
// Schedule Local Notification
self.scheduleLocalNotification()
case .denied:
print("Application Not Allowed to Display Notifications")
case .provisional:
print("provisional")
}
}
previous version
You can copy&paste this code in didFinishLaunchingWithOptions method:
Create the action
// NOTFICATION
let incrementAction = UIMutableUserNotificationAction()
incrementAction.identifier = "HI_ACTION"
incrementAction.title = "Hi!"
incrementAction.activationMode = UIUserNotificationActivationMode.Background
incrementAction.authenticationRequired = false
incrementAction.destructive = false
Create the category
let counterCategory = UIMutableUserNotificationCategory()
counterCategory.identifier = "HELLO_CATEGORY"
Associate action and category
counterCategory.setActions([incrementAction],
forContext: UIUserNotificationActionContext.Default)
counterCategory.setActions([incrementAction],
forContext: UIUserNotificationActionContext.Minimal)
Registration
let categories = NSSet(object: counterCategory) as! Set<UIUserNotificationCategory>
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Sound], categories: categories)
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
The last step is only for demostration
let notification = UILocalNotification()
notification.alertBody = "Hey!"
notification.soundName = UILocalNotificationDefaultSoundName
notification.fireDate = NSDate(timeIntervalSinceNow: 5)
notification.category = "HELLO_CATEGORY"
UIApplication.sharedApplication().scheduleLocalNotification(notification)
When the app is launched you have to push CMD+L
Here's a link to an example

Related

Can't get variable from Notification?

I want to write a project has a notification can print data which stored in main viewcontroller, but when notification fired, always get empty data.
In the ViewController
arrPerson = [String]()
override func viewDidLoad() {
super.viewDidLoad()
arrPerson.append("Peter")
arrPerson.append("Jack")
setNotification()
}
func printTheName() {
print("In printTheName")
print("arrPerson:\(arrPerson.count)")
for x in arrPerson {
print(x)
}
}
func setNotification() {
let notification = UNMutableNotificationContent()
notification.title = "title"
notification.subtitle = ""
notification.body = "body"
notification.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60, repeats: true)
let request = UNNotificationRequest(identifier: "check", content: notification, trigger: trigger)
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.add(request) { (error) in
if error != nil {
print("notificationCenter.add ERROR:\(error)")
// Handle any errors.
}
}
}
In the Appdelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {
print("In userNotificationCenter")
ViewController().printTheName()
}
The code will print out "userNotificationCenter","In printTheName","arrPerson:0".
I need get data from notification. Why arrPerson.count is 0 when call printTheName from notification?
Try to programatically instantiate the viewController using a window in your appdelegate.
You will be able to fetch the count then in your notification center.Or since your viewController class is the landing screen, Call the userNotifationCenter method in your main Class and not here.

iOS 10 UNNotification doesn't show alert

The code is very easy:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: { (granted, error) in
if !granted {
print("Not allowed")
}
})
let content = UNMutableNotificationContent()
content.title = "Alert"
content.sound = UNNotificationSound.default()
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 20, repeats: false)
let request = UNNotificationRequest(identifier: "test", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
return true
}
It works well on iOS 11, like this:
but on iOS 10, the alert doesn't show.
On both iOS 10 and iOS 11, the sound did appear.
My Xcode version is 9.2(9C40b)
Any help is appreciated.
Try to add the body of notification like this
content.body = "Any text/Blank Space"
Hope this will help you

TableView not updating

I'm currently struggling with getting my TableView to update after I finish performing some functions called in viewDidLoad and viewDidAppear. I tried using self.tableView.reloadData() at the end of my viewDidLoad but it didn't work and upon reloading the tab, the app would crash.
Here is some of my code (I'm trying to fetch events from a Google Calendar and display it in a TableView). I'm trying to display an array of strings named listOfEvents and it is being populated after the tableView is already loaded.
I also tried adding self.tableView.reloadData() at the end of my fetchEvents() but it also killed my app upon reloading the tab
class CalendarViewController: UITableViewController {
var listOfEvents: [String] = []
private let kKeychainItemName = "Google Calendar API"
private let kClientID = "clientID"
// If modifying these scopes, delete your previously saved credentials by
// resetting the iOS simulator or uninstall the app.
private let scopes = [kGTLAuthScopeCalendarReadonly]
private let service = GTLServiceCalendar()
let output = UITextView()
// When the view loads, create necessary subviews
// and initialize the Google Calendar API service
override func viewDidLoad() {
super.viewDidLoad()
if let auth = GTMOAuth2ViewControllerTouch.authForGoogleFromKeychainForName(
kKeychainItemName,
clientID: kClientID,
clientSecret: nil) {
service.authorizer = auth
}
}
// When the view appears, ensure that the Google Calendar API service is authorized
// and perform API calls
override func viewDidAppear(animated: Bool) {
if let authorizer = service.authorizer,
canAuth = authorizer.canAuthorize where canAuth {
fetchEvents()
} else {
presentViewController(
createAuthController(),
animated: true,
completion: nil
)
}
}
// Construct a query and get a list of upcoming events from the user calendar
func fetchEvents() {
let query = GTLQueryCalendar.queryForEventsListWithCalendarId("primary")
query.maxResults = 10
query.timeMin = GTLDateTime(date: NSDate(), timeZone: NSTimeZone.localTimeZone())
query.singleEvents = true
query.orderBy = kGTLCalendarOrderByStartTime
service.executeQuery(
query,
delegate: self,
didFinishSelector: "displayResultWithTicket:finishedWithObject:error:"
)
}
// Display the start dates and event summaries in the UITextView
func displayResultWithTicket(
ticket: GTLServiceTicket,
finishedWithObject response : GTLCalendarEvents,
error : NSError?) {
if let error = error {
showAlert("Error", message: error.localizedDescription)
return
}
var eventString = ""
if let events = response.items() where !events.isEmpty {
for event in events as! [GTLCalendarEvent] {
let start : GTLDateTime! = event.start.dateTime ?? event.start.date
let startString = NSDateFormatter.localizedStringFromDate(
start.date,
dateStyle: .ShortStyle,
timeStyle: .ShortStyle
)
eventString += "\(startString) - \(event.summary)\n"
// An array holding all my upcoming events
listOfEvents.append("\(startString) - \(event.summary)")
print(listOfEvents)
}
} else {
eventString = "No upcoming events found."
}
output.text = eventString
self.tableView.reloadData()
}
// Creates the auth controller for authorizing access to Google Calendar API
private func createAuthController() -> GTMOAuth2ViewControllerTouch {
let scopeString = scopes.joinWithSeparator(" ")
return GTMOAuth2ViewControllerTouch(
scope: scopeString,
clientID: kClientID,
clientSecret: nil,
keychainItemName: kKeychainItemName,
delegate: self,
finishedSelector: "viewController:finishedWithAuth:error:"
)
}
// Handle completion of the authorization process, and update the Google Calendar API
// with the new credentials.
func viewController(vc : UIViewController,
finishedWithAuth authResult : GTMOAuth2Authentication, error : NSError?) {
if let error = error {
service.authorizer = nil
showAlert("Authentication Error", message: error.localizedDescription)
return
}
service.authorizer = authResult
dismissViewControllerAnimated(true, completion: nil)
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.Default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.listOfEvents.count)
return self.listOfEvents.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("Events Cell", forIndexPath: indexPath) as UITableViewCell
var event = ""
event = listOfEvents[indexPath.row]
cell.textLabel?.text = event
return cell
}
}
I would appreciate any help and insight :-) Thanks so much!
After output.text = eventString, you should reload the tableview.

Cannot convert value of type 'NSData' to expected argument type 'String'

I'm writing codes on swift in XCode. This is the code:
import UIKit
import Foundation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
let notificationTypes : UIUserNotificationType = [.Alert, .Badge, .Sound]
let notificationSettings : UIUserNotificationSettings = UIUserNotificationSettings(forTypes: notificationTypes, categories: nil)
UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings)
return true
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings)
{
UIApplication.sharedApplication().registerForRemoteNotifications()
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
print("TOKEN:", deviceToken);
let token = String(data: deviceToken, encoding: NSUTF8StringEncoding);
let myUrl = NSURL(fileURLWithPath: "http://......php?id=" + token);
print("URL:",fileURLWithPath: "http://......php?id=" + token);
}
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
print(error.localizedDescription)
}
/* func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
}*/
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
Compiler gives me an error on the func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {" and it says me "Cannot convert value of type 'NSData' to expected argument type 'String'"... but I can't understand the way to solve the problem. So, can somebody help me to correct the error?
var charSet: NSCharacterSet = NSCharacterSet(charactersInString: "<>")
var tokenStr: String = (deviceToken.description as NSString)
.stringByTrimmingCharactersInSet(characterSet)
.stringByReplacingOccurrencesOfString( " ", withString: "") as String
print(deviceTokenString)
Try that
USe this, for sending token to the server. Worked well for me
var token: String = "\(deviceToken)"
let rawtoken = token.stringByReplacingOccurrencesOfString(">", withString: "")
let cleantoken = rawtoken.stringByReplacingOccurrencesOfString("<", withString: "")
var finaltoken = cleantoken.stringByReplacingOccurrencesOfString(" ", withString: "")
Final token is the one that you are supposed to use.
Source was Udemy online courses.
Note that the token is BINARY, so you cannot easily convert it to a string (no UTF8!).
It is a good practice to convert it to hex:
let tokenChars = UnsafePointer<CChar>(deviceToken.bytes)
var tokenString = ""
for var i = 0; i < deviceToken.length; i++ {
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
}
print("Push token: \(tokenString)")

Swift 3D Touch Quick Action not loading requested url

I'm new to Swift, and trying my hands with UIWebView app that loads default url, with option to perform quick action and load a different url.
Problem is when I request the quick action url, code executes but the new url is not loading. So I'm missing something in the flow somewhere.
Here is the code:
import UIKit
import WebKit
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView: UIWebView!
override func loadView() {
super.loadView()
self.webView = UIWebView()
self.view = self.webView!
}
override func viewDidLoad() {
print("view did load")
super.viewDidLoad()
let url = NSURL(string: "google.com")
let req = NSURLRequest(URL:url!)
webView.loadRequest(req)
webView.delegate = self
}
func loadUrl2() {
loadView()
let url = NSURL(string: "example.com")
print(url)
let req = NSURLRequest(URL:url!)
self.webView!.loadRequest(req)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I was experimenting and added loadView to loadUrl2, as I was getting
fatal error: unexpectedly found nil while unwrapping an Optional value
before that.
Edited to Include loading secondary link:
Here are the changes and files you'll need to make to the App Delegate
enum ShortcutIdentifier: String {
case OpenNewLink
case OpenBetterLink
init?(fullIdentifier: String) {
guard let shortIdentifier = fullIdentifier.componentsSeparatedByString(".").last else {
return nil
}
self.init(rawValue: shortIdentifier)
}
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsAnnotationKey] as? UIApplicationShortcutItem {
handleShortcut(shortcutItem)
return false
}
return true
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
completionHandler(handleShortcut(shortcutItem))
}
private func handleShortcut(shortcutItem: UIApplicationShortcutItem) -> Bool {
let shortcutType = shortcutItem.type
guard let ShortcutIdentifier = ShortcutIdentifier(fullIdentifier: shortcutType) else {
return false
}
return selectLinkForIdentifier(ShortcutIdentifier)
}
private func selectLinkForIdentifier(identifier: ShortcutIdentifier) -> Bool {
guard let mainView = self.window?.rootViewController as? ViewController else {
return false
}
switch identifier {
case .OpenNewLink:
mainView.urlString = "http://www.bing.com"
mainView.loadWebView(mainView.urlString)
return true
case.OpenBetterLink:
mainView.urlString = "http://www.duckduckgo.com"
mainView.loadWebView(mainView.urlString)
return true
}
}
I also made changes in the MainVC
class ViewController: UIViewController, UIWebViewDelegate {
#IBOutlet var webView: UIWebView!
var urlString: String? = nil
override func viewDidLoad() {
super.viewDidLoad()
setUpWebView()
webView.delegate = self
view.addSubview(webView)
}
func setUpWebView() {
webView = UIWebView()
webView.frame = CGRectMake(0, 0, view.frame.width, view.frame.height)
loadWebView(urlString)
}
func loadWebView(var urlString: String?) {
if urlString == nil {
urlString = "http://www.google.com"
}
let url = NSURL(string: urlString!)
let req = NSURLRequest(URL:url!)
webView.loadRequest(req)
}
}
Be sure to add NSAppTransportSecurity dictionary to your .plist and add NSAllowsArbitraryLoads key set to YES.
I tested it and it should work for you.

Resources