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.
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'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....
}
I finally manage to do universal links, if I tap the link it will open my app. but it doesn't show up in continue user activity or source application. Because I want to redirect it to the specific view controller. For example if I open "https://www.myWeb.com/forgotPassword", it will redirect to forgotPasswordViewController, and I need to read the link.
Why doesn't it show up in continue user activity?
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
print(url)
let urlHost : String = url.host as String!
print("HELLO")
let main : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let inner : ResetPasswordViewController = main.instantiateViewController(withIdentifier: "ResetPasswordViewController") as! ResetPasswordViewController
self.window?.rootViewController = inner
self.window?.makeKeyAndVisible()
return true;
}
func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
print(url)
print("HELLO WORLD")
}
return true
}
And this is what happened with the source application; can someone tell me why it becomes like that?
SOLVED
it's just because of type Data, i put AnyObject instead of Any.
but the compiler doesn't tell me if AnyObject can't be used
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
print("Continue User Activity: ")
if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
let url = userActivity.webpageURL!
print(url.absoluteString)
//handle url
}
return true
}
My ApplicationDelegate:
let customURLScheme = "dlscheme"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FIROptions.default().deepLinkURLScheme = self.customURLScheme
FIRApp.configure()
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
return application(app, open: url, sourceApplication: nil, annotation: [:])
}
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
let dynamicLink = FIRDynamicLinks.dynamicLinks()?.dynamicLink(fromCustomSchemeURL: url)
if let dynamicLink = dynamicLink {
let message = generateDynamicLinkMessage(dynamicLink)
if #available(iOS 8.0, *) {
showDeepLinkAlertView(withMessage: message)
}
return true
}
if #available(iOS 8.0, *) {
showDeepLinkAlertView(withMessage: "openURL:\n\(url)")
} else {
}
return false
}
#available(iOS 8.0, *)
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool {
guard let dynamicLinks = FIRDynamicLinks.dynamicLinks() else {
return false
}
let handled = dynamicLinks.handleUniversalLink(userActivity.webpageURL!) { (dynamiclink, error) in
let message = self.generateDynamicLinkMessage(dynamiclink!)
self.showDeepLinkAlertView(withMessage: message)
}
return handled
}
func generateDynamicLinkMessage(_ dynamicLink: FIRDynamicLink) -> String {
let matchConfidence: String
if dynamicLink.matchConfidence == .weak {
matchConfidence = "Weak"
} else {
matchConfidence = "Strong"
}
let message = "App URL: \(dynamicLink.url)\nMatch Confidence: \(matchConfidence)\n"
return message
}
#available(iOS 8.0, *)
func showDeepLinkAlertView(withMessage message: String) {
let okAction = UIAlertAction.init(title: "OK", style: .default) { (action) -> Void in
print("OK")
}
let alertController = UIAlertController.init(title: "Deep-link Data", message: message, preferredStyle: .alert)
alertController.addAction(okAction)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
Here is my code. I am working with firebase dynamic link, and implemented it like this tutorial:
https://firebase.google.com/docs/dynamic-links/ios
And this sample:
https://github.com/firebase/quickstart-ios/blob/master/dynamiclinks/DynamicLinksExampleSwift/AppDelegate.swift
Its working well when my app is in background. Open the app and show alert with url when I tap on dynamic link.
But in the case my app is inactive (not running), it not working. Just open the app and do nothing.
And my question: How can i handle dynamic link while app is inactive?
Sorry about my english
Swift 5
You can handle it in such way:
AppDelegate.swift
import Firebase
import FirebaseDynamicLinks
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// ... your other code here
FirebaseApp.configure()
let activityKey = NSString(string: "UIApplicationLaunchOptionsUserActivityKey")
if let userActivityDict = launchOptions?[.userActivityDictionary] as? [NSObject : AnyObject], let userActivity = userActivityDict[activityKey] as? NSUserActivity, let webPageUrl = userActivity.webpageURL {
DynamicLinks.dynamicLinks().handleUniversalLink(webPageUrl) { (dynamiclink, error) in
// do some stuff with dynamiclink
}
}
return true
}
I have do like this...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
if let option = launchOptions
{
if option.keys.contains(UIApplicationLaunchOptionsKey.userActivityDictionary)
{
if let userActivityDict = option[UIApplicationLaunchOptionsKey.userActivityDictionary] as? [AnyHashable:Any]
{
if userActivityDict.keys.contains(UIApplicationLaunchOptionsKey.userActivityType)
{
if let userActivity = userActivityDict["UIApplicationLaunchOptionsUserActivityKey"] as? NSUserActivity {
if let webpageURL = userActivity.webpageURL
{
// Write your code here.
}
}
}
}
}
}
}
I had a problem when application(_:open:options:) wasn't called when the app was in background. The Firebase SDK swizzles methods and that was the reason.
I solved this problem by setting FirebaseAppDelegateProxyEnabled = NO in Info.plist
For more detailed information on how it works and what it affects you can read here https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in
I am trying to add 3D Touch Shortcuts to an application, I have managed to have the shortcuts appear when using 3DTouch on the app icon from the homescreen; however when using the shortcut the application crashes on load and I am unsure why.
I have managed to get the application to load for the bookmarks shortcut but it does not initiate the BookmarksViewController, it just loads the InitialViewController.
The application is embedded within a UITabBarController and a UINavigationController for each Tab. Both View Controllers I am trying to load are in different tabs but the first view in the navigation controller stack.
Does anyone know where I am going wrong ?
info.plist file
App Delegate
enum ShortcutItemType: String {
case Bookmarks
case Favourites
init?(shortcutItem: UIApplicationShortcutItem) {
guard let last = shortcutItem.type.componentsSeparatedByString(".").last else { return nil }
self.init(rawValue: last)
}
var type: String {
return NSBundle.mainBundle().bundleIdentifier! + ".\(self.rawValue)"
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
handleShortcutItem(shortcutItem)
}
return true
}
private func handleShortcutItem(shortcutItem: UIApplicationShortcutItem) {
if let rootViewController = window?.rootViewController, let shortcutItemType = ShortcutItemType(shortcutItem: shortcutItem) {
let sb = UIStoryboard(name: "main", bundle: nil)
let favouritesVC = sb.instantiateViewControllerWithIdentifier("FavouritesVC") as! FavouritesTableViewController
let bookmarksVC = sb.instantiateViewControllerWithIdentifier("BookmarksVC") as! BookmarksNotesViewController
switch shortcutItemType {
case .Bookmarks:
rootViewController.presentViewController(bookmarksVC, animated: true, completion: nil)
break
case .Favourites:
rootViewController.presentViewController(favouritesVC, animated: true, completion: nil)
break
}
}
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
handleShortcutItem(shortcutItem)
}
}
Storyboard ID
These are the StoryboardID for the View Controllers.
I think you forgot the 's' in com.appName.Bookmark
Or you need to remove the 's' from Bookmarks here:
enum ShortcutItemType: String {
case Bookmarks
case Favourites
Instead, try using a shortcut like this:
if shortcutItem.type == "com.appName.Bookmark"
#available(iOS 9.0, *)
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
if shortcutItem.type == "com.appName.Bookmark" {
}
}