Reputation: 775
I'm implementing some 3D touch quick actions for my iOS 9 app in swift, and I have a curious issue. When my app is in the background and I launch with the quick action, everything goes as planned. When my app is totally dead (i.e. I killed it from the multitasking menu), and I launch with the quick action, the app crashes. I'm having trouble debugging this as once I kill the app, the debug session in Xcode gets detached. Is there a way for me to connect to the app to debug like normal, or is there something in my code that would be causing it? Thanks in advance.
Code:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
var launchedFromShortCut = false
//Check for ShortCutItem
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem
{
launchedFromShortCut = true
self.handleShortCutItem(shortcutItem)
}
return !launchedFromShortCut
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void)
{
self.handleShortCutItem(shortcutItem)
}
func handleShortCutItem(shortcutItem: UIApplicationShortcutItem)
{
//Get type string from shortcutItem
if let shortcutType = ShortcutType.init(rawValue: shortcutItem.type)
{
//Get root navigation viewcontroller and its first controller
let rootNavigationViewController = window!.rootViewController as? UINavigationController
if let rootViewController = rootNavigationViewController?.viewControllers.first as! LaunchViewController?
{
//Pop to root view controller so that approperiete segue can be performed
rootNavigationViewController?.popToRootViewControllerAnimated(false)
switch shortcutType
{
case .Compose:
rootViewController.shouldCompose()
break
}
}
}
}
Thanks!
Upvotes: 31
Views: 11810
Reputation: 21
For my Ionic devs here in 2024 looking for solutions, this is what i did:
Use perform(#selector(handleShortcut(_:)), with: shortcutItem, afterDelay: 0.5)
to add a delay in handling the shortcut, allowing the app to finish initializing.
In application(_:didFinishLaunchingWithOptions:)
, store the shortcut for handling later in applicationDidBecomeActive(_:)
.
Pree a Sample Below:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var launchedShortcutItem: UIApplicationShortcutItem?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Check for a shortcut item in launch options
if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem {
launchedShortcutItem = shortcutItem
}
return true
}
func applicationDidBecomeActive(_ application: UIApplication) {
// If there was a shortcut item on cold start, handle it with a delay
if let shortcut = launchedShortcutItem {
self.perform(#selector(handleShortcut(_:)), with: shortcut, afterDelay: 0.5)
launchedShortcutItem = nil
}
}
@objc func handleShortcut(_ shortcutItem: UIApplicationShortcutItem) {
// Example: Handle different shortcut types
switch shortcutItem.type {
case "pay-bills":
performActionForPayBills()
case "airtime-Topup":
performActionForAirtimeTopup()
case "transfer-money":
performActionForTransferMoney()
case "snap-2-pay":
performActionForSnap2Pay()
default:
break
}
}
func application(
_ application: UIApplication,
performActionFor shortcutItem: UIApplicationShortcutItem,
completionHandler: @escaping (Bool) -> Void
) {
// Handle shortcuts when the app is already running
self.perform(#selector(handleShortcut(_:)), with: shortcutItem, afterDelay: 0.5)
completionHandler(true)
}
// MARK: - Action Methods for Shortcuts
private func performActionForPayBills() {
print("Shortcut: Pay Bills")
let bridge = (self.window?.rootViewController as! CAPBridgeViewController).bridge
bridge?.webView?.evaluateJavaScript("handleQuickAction('pay-bills')")
}
private func performActionForAirtimeTopup() {
print("Shortcut: Airtime Topup")
let bridge = (self.window?.rootViewController as! CAPBridgeViewController).bridge
bridge?.webView?.evaluateJavaScript("handleQuickAction('airtime-Topup')")
}
private func performActionForTransferMoney() {
print("Shortcut: Transfer Money")
let bridge = (self.window?.rootViewController as! CAPBridgeViewController).bridge
bridge?.webView?.evaluateJavaScript("handleQuickAction('transfer-money')")
}
private func performActionForSnap2Pay() {
print("Shortcut: Snap2Pay")
let bridge = (self.window?.rootViewController as! CAPBridgeViewController).bridge
bridge?.webView?.evaluateJavaScript("handleQuickAction('snap-2-pay')")
}
}
Upvotes: 0
Reputation: 726
XCode 11.6, Swift 5
We can attach a process at runtime. XCode will wait until the process is started and will get attached to it when an App is launched manually.
XCode -> Debug -> Attach to process by PID or Name -> ("Enter the name of an app in the pop-up")
Directions:
P.S: If you are using SceneDelegate a shortcutItem can be found in
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
switch connectionOptions.shortcutItem?.localizedTitle {
case "Search":
break
case "DoSomething":
break
default:
break
}
}
Happy Debugging :)
Upvotes: 0
Reputation: 1316
Now, if you turn on debugging and run your app, Xcode will wait for you to launch your app from the home screen so you are able to test launching it using a 3D Touch Shortcut Item.
Upvotes: 67
Reputation: 351
For Swift 4.2
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
var isLaunchedFromQuickAction = false
if let shortcutItem = launchOptions?[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {
isLaunchedFromQuickAction = true
handleQuickAction(shortcutItem: shortcutItem)
}
return isLaunchedFromQuickAction
}
Upvotes: 3
Reputation: 11
Replace your didfinishlaunching method with this one.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
if let shortcutItem =
launchOptions?[UIApplicationLaunchOptionsShortcutItemKey]
as? UIApplicationShortcutItem {
handleShortcut(shortcutItem)
return false
}
return true
}
Upvotes: 1
Reputation: 1344
I tried all the above, and it didn't solve the problem than I tried handling the shortcut after delay in handleShortcut method:
self.performSelector("action1", withObject: self, afterDelay: 0.5)
and added a method for every action, and it worked like a charm
Upvotes: 1
Reputation: 2913
I finally got this working. Here's what my AppDelegate.swift file ended up as;
class AppDelegate: UIResponder, UIApplicationDelegate {
// Properties
var window: UIWindow?
var launchedShortcutItem: UIApplicationShortcutItem?
func applicationDidBecomeActive(application: UIApplication) {
guard let shortcut = launchedShortcutItem else { return }
handleShortcut(shortcut)
launchedShortcutItem = nil
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
var shouldPerformAdditionalDelegateHandling = true
// If a shortcut was launched, display its information and take the appropriate action
if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem {
launchedShortcutItem = shortcutItem
// This will block "performActionForShortcutItem:completionHandler" from being called.
shouldPerformAdditionalDelegateHandling = false
}
return shouldPerformAdditionalDelegateHandling
}
func handleShortcut( shortcutItem:UIApplicationShortcutItem ) -> Bool {
// Construct an alert using the details of the shortcut used to open the application.
let alertController = UIAlertController(title: "Shortcut Handled", message: "\"\(shortcutItem.localizedTitle)\"", preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(okAction)
// Display an alert indicating the shortcut selected from the home screen.
window!.rootViewController?.presentViewController(alertController, animated: true, completion: nil)
return handled
}
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
completionHandler(handleShortcut(shortcutItem))
}
Much of this was taken from Apple's sample code for UIApplicationShortcuts, and while I'm having my app launch an alert to prove that it is recognizing the proper shortcut was chosen, this could be adapted to your code to pop the view controller.
I think the func applicationDidBecomeActive
was the critical part that I was missing, and removing the self.handleShortCut(shortcutItem)
from didFinishLaunchingWithOptions
(otherwise it was calling handleShortCut
twice, it seemed).
Upvotes: 28