I'm working on an app that receives data from a remote notification, I'm trying to pass that data from didFinishLaunchingWithOptions to my ViewController using Notificationcenter when opening the app through tapping on the notification using launchOptions. The problem is that the observer on my viewDidAppear is not getting any data.
This is my code on the didFinishLaunchingWithOptions method:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
let nameSchool = remoteNotification["name_school" as! String]
NotificationCenter.default.post(name: Notification.Name.nameSchool, object: nameSchool)
}
}
And the observer in the viewDidAppear method:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NotificationCenter.default.addObserver(forName: Notification.Name.nameSchool, object: nil, queue: OperationQueue.main) { (nameSchool) in
let schoolName = nameSchool.object as! String
self.messagePopup(message: "Data received")
}
}
Since your application(,didFinishLaunchingWithOptions:) will be called before the viewDidAppear (as per your comment), then you will have to temporarily store the result you get from the function until your code can retrieve it later.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var remoteNotificationAtLaunch: [AnyHashable: Any]?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.remoteNotificationAtLaunch = launchOptions?[.remoteNotification] as? [AnyHashable : Any]
}
...
}
Obviously keep the part you already have in your AppDelegate that generates the post to NotificationCenter when a remote notification is received. Then in your view controller, update your viewDidAppear...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
observeNotifications()
checkForNotificationAtLaunch()
}
private func observeNotifications() {
NotificationCenter.default.addObserver(forName: Notification.Name.nameSchool, object: nil, queue: OperationQueue.main) { (nameSchool) in
let schoolName = nameSchool.object as! String
self.processNotification(schoolName: schoolName)
}
}
private func checkForNotificationAtLaunch() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
if let notificationAtLaunch = appDelegate.remoteNotificationAtLaunch,
let schoolName = notificationAtLaunch["name_school"] as? String {
processNotification(schoolName: schoolName)
}
}
}
private func processNotification(schoolName: String) {
self.messagePopup(message: "data received")
// do something with schoolName....
}
Related
I'm trying to implement a 3D touch command where if the user presses "New scan", then the view controller ProcessedImageViewController is called. I have already set up the Info.plist to create the quick option, but I am having trouble actually calling the ProcessedImageViewController when "New scan" is pressed.
Here is my code from the AppDelegate.swift:
var launchedShortcutItem: UIApplicationShortcutItem?
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if let shortcutItem = launchOptions?[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem{
launchedShortcutItem = shortcutItem
}
return true
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
completionHandler(handleShortcutItem(item: shortcutItem))
}
func applicationDidBecomeActive(_ application: UIApplication) {
guard let shortcutItem = launchedShortcutItem else { return }
//If there is any shortcutItem,that will be handled upon the app becomes active
_ = handleShortcutItem(item: shortcutItem)
//We make it nil after perfom/handle method call for that shortcutItem action
launchedShortcutItem = nil
}
func handleShortcutItem(item: UIApplicationShortcutItem) -> Bool {
var handled = false
// Verify that the provided shortcutItem's type is one handled by the application.
let mainStoryboard = UIStoryboard.init(name: "Main", bundle: Bundle.main)
var reqVC: UIViewController!
reqVC = mainStoryboard.instantiateViewController(withIdentifier: "ProcessedImageViewController") as! ProcessedImageViewController
handled = true
if let homeVC = self.window?.rootViewController as? UINavigationController {
homeVC.pushViewController(reqVC, animated: true)
} else {
return false
}
return handled
}
When I try to click on "New scan" in the Quick Actions menu, I only get taken to the Root View controller (not ProcessedImageViewController).
I am trying to migrate my code to Swift 3, and came across an error regarding trying to handle 3D Touch shortcuts..
I get the following error
Performing segue using 3D Touch Shortcut - Ambiguous Reference To
Member 'Subscript'
For the following line
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: Any]?) -> Bool {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
handleShortcutItem(shortcutItem)
}
}
I am not sure why I am getting this, does anyone else know ?
Here's how I am handling the shortcuts
enum ShortcutItemType: String {
case First
case Second
case Third
init?(shortcutItem: UIApplicationShortcutItem) {
guard let last = shortcutItem.type.components(separatedBy: ".").last else { return nil }
self.init(rawValue: last)
}
var type: String {
return Bundle.main.bundleIdentifier! + ".\(self.rawValue)"
}
}
fileprivate func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let rootNavController = rootViewController.childViewControllers.first as! UINavigationController
let viewController = rootNavController.childViewControllers[1]
switch shortcutItemType {
case .First:
viewController.performSegue(withIdentifier: "firstSegue", sender: self)
break
case .Second:
viewController.performSegue(withIdentifier: "secondSegue", sender: self)
break
case .Third:
//Segue to view controller from first then perform another segue to a modal view.
viewController.performSegue(withIdentifier: "thirdSegue", sender: self)
break
}
}
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
handleShortcutItem(shortcutItem)
}
The method header of application(_:didFinishLaunchingWithOptions:) has changed to:
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil)
The symbol UIApplicationLaunchOptionsShortcutItemKey has been replaced with UIApplicationLaunchOptionsKey.shortcutItem.
And this may be another issue, but application(_:performActionFor:completionHandler:) needs to have this header:
func application(_ application: UIApplication,
performActionFor shortcutItem: UIApplicationShortcutItem,
completionHandler: #escaping (Bool) -> Void)
Try fixing all of them.
When the notification is clicked on I want it to open a specific page, but for some reason it keeps crashing with the error fatal error: unexpectedly found nil while unwrapping an Optional value. Why is this error happening? Am I opening the page correctly?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let launchOptions = launchOptions as? [String : AnyObject] {
if let notificationDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] as? [NSObject : AnyObject] {
self.application(application, didReceiveRemoteNotification: notificationDictionary)
let text = launchOptions["aps"]!["alert"]
print(text)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
self.window?.rootViewController = viewController
}
}
return true
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
let text = userInfo["aps"]!["alert"]
print(text)
let viewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
self.window?.rootViewController = viewController
}
Hope this will help :
Find visible view controller when receive Notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let currentViewControlelr :UIViewController = topViewController(UIApplication.sharedApplication().keyWindow?.rootViewController)!;
if(currentViewController == PlayerViewController()){
//Implement other function according to your needs
}else{
//If any other view controller at top of stack, then create object of Player View controller and push to that
let playerViewController = storyBoard.instantiateViewControllerWithIdentifier("Player") as UIViewController
currentViewControlelr.navigationController?.pushViewController(playerViewController, animated: true)
NSLog("UserInfo : %#",userInfo);
}
Helper Method to get Top ViewController which is visible at the moment
func topViewController(base: UIViewController? ) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController {
if let selected = tab.selectedViewController {
return topViewController(selected)
}
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
Here I have set the code for URL scheme:
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
if let userUrl = String(url) as String? {
print("\(userUrl)")
if (userUrl == "count://fromClipBoard") {
NSNotificationCenter.defaultCenter().postNotificationName("com.getContentFromClipBoard", object: self)
}
}
return false
}
And add the following code to my ViewController:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setContentFromClipBoard", name: "com.getContentFromClipBoard", object: nil)
//............
}
func setContentFromClipBoard() {
tv.text = "WAHAHA"
if let clipBoard = UIPasteboard.generalPasteboard().string {
tv.text = clipBoard
}
}
And when my app is not fully quitted, com.getContentFromClipBoard called well and tv.text become clipBoard.
However, when I fully quit the app and then use this URL scheme, the com.getContentFromClipBoard isn't get called and tv.text remain empty.
So how can I fix it? Thanks!
Fixed by checking UIApplicationLaunchOptionsURLKey too inside didFinishLaunchingWithOptions.
(Delaying is waiting the view to load)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let userUrl = launchOptions?[UIApplicationLaunchOptionsURLKey] as? NSURL {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2 * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
if (userUrl.absoluteString == "count://fromClipBoard") {
NSNotificationCenter.defaultCenter().postNotificationName("com.getContentFromClipBoard", object: self)
}
})
}
}
I'm building an app with Swift that receives push notifications. I am sending custom values inside the JSON.
I am opening the app through the notification, so I know that I have to do this inside "didFinishLaunchingWithOptions" and read the value from "launchOptions".
How can I read those values and use them on my app.
Many thanks.
Here's what works for me in SWIFT 2 when your app is not launched. The code is not quite elegant because of the optional bindings. But it works.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// if launched from a tap on a notification
if let launchOptions = launchOptions {
if let userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] {
if let action = userInfo["action"], id = userInfo["id"] {
let rootViewController = self.window!.rootViewController as! ViewController
let _ = setTimeout(5.0, block: { () -> Void in
rootViewController.openNotification(action as! String, id: id as! String)
})
}
}
}
return true
}
In the application:didReceiveRemoteNotification:fetchCompletionHandler, The custom data is in passed on to the didReceiveRemoteNotification, which is an NSDictionary. The details that you want to retrieve is probably on the "aps" key of the userInfo.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: NSDictionary!)
{
var notificationDetails: NSDictionary = userInfo.objectForKey("aps") as NSDictionary
}
When the app is not launched, you will need to get it from the application:didFinishedLaunchWithOptions,
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
if let launchOpts = launchOptions {
var notificationDetails: NSDictionary = launchOpts.objectForKey(UIApplicationLaunchOptionsRemoteNotificationKey) as NSDictionary
}
return true
}
EDIT: Remote Notification Fix syntax
This is the Oliver Zhang response which is updated for Swift 5.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// if launched from a tap on a notification
if let launchOptions = launchOptions {
if let userInfo = launchOptions[UIApplication.LaunchOptionsKey.remoteNotification] {
}
}
return true
}