I am trying to create an iOS application, using the newest version of Swift, where a user can force touch on our app icon on their home screen. This then displays the options to open the Apple Calendar, Notes, and Maps apps. However, when we tap these, only our app opens and not the respective Apple app after.
Here is my LauncherApp.swift file:
import SwiftUI
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
override init() {
super.init()
print("AppDelegate initialized")
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("Application did finish launching with options.")
self.window = UIWindow()
self.window?.rootViewController = UIHostingController(rootView: ContentView())
self.window?.makeKeyAndVisible()
if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem {
print("Launching from Quick Action: \(shortcutItem.type)")
handleShortcutItem(shortcutItem)
return false
}
return true
}
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
print("Received a Quick Action while running: \(shortcutItem.type)")
handleShortcutItem(shortcutItem)
completionHandler(true)
}
private func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem) {
print("Handling Shortcut Item: \(shortcutItem.type)")
switch shortcutItem.type {
case "com.launcher.openMaps":
print("Opening Maps...")
openMaps()
case "com.launcher.openCalendar":
print("Opening Calendar...")
openCalendar()
case "com.launcher.openNotes":
print("Opening Notes...")
openNotes()
default:
print("Unhandled Shortcut Item: \(shortcutItem.type)")
}
}
private func openMaps() {
openApp(withURLScheme: "maps://")
}
private func openCalendar() {
openApp(withURLScheme: "calshow://")
}
private func openNotes() {
openApp(withURLScheme: "mobilenotes://")
}
private func openApp(withURLScheme urlScheme: String) {
print("Attempting to open app with URL Scheme: \(urlScheme)")
guard let url = URL(string: urlScheme) else {
print("Invalid URL Scheme: \(urlScheme)")
return
}
if UIApplication.shared.canOpenURL(url) {
print("Opening app with URL Scheme: \(urlScheme)")
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
print("Cannot open URL Scheme: \(urlScheme)")
}
}
}
@main
struct LauncherApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Here is my Info.plist file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>maps</string>
<string>mobilenotes</string>
<string>calshow</string>
</array>
<key>UIApplicationShortcutItems</key>
<array>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeLocation</string>
<key>UIApplicationShortcutItemTitle</key>
<string>Open Maps</string>
<key>UIApplicationShortcutItemType</key>
<string>com.launcher.openMaps</string>
</dict>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeDate</string>
<key>UIApplicationShortcutItemTitle</key>
<string>Open Calendar</string>
<key>UIApplicationShortcutItemType</key>
<string>com.launcher.openCalendar</string>
</dict>
<dict>
<key>UIApplicationShortcutItemIconType</key>
<string>UIApplicationShortcutIconTypeCompose</string>
<key>UIApplicationShortcutItemTitle</key>
<string>Open Notes</string>
<key>UIApplicationShortcutItemType</key>
<string>com.launcher.openNotes</string>
</dict>
</array>
</dict>
</plist>
Here is ContentView.swift, which opens Maps perfectly fine:
import SwiftUI
struct ContentView: View {
var body: some View {
Button("Test Open Maps") {
openApp(withURLScheme: "maps://")
}
}
func openApp(withURLScheme urlScheme: String) {
guard let url = URL(string: urlScheme) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I am new to iOS development so sorry for just dumping the code. I have tried testing in a simulator and on my iPhone, but it doesn't work on either.
When running under iOS 13 and later, the App Delegate is not used to handle shortcuts. You now need to handle shortcut items in the Scene Delegate.
Add a file named SceneDelegate.swift to your project with the following contents:
In your AppDelegate.swift, remove the private functions for handling the shortcuts (all of that code is now in
SceneDelegate
). Remove theperformActionFor
app delegate method since it isn't used as of iOS 13. Remove theif let...
block from thedidFinishLaunchingWithOptions
app delegate method since that is also handled in the scene delegate.For a UIKit app, that is all that is needed.
For a SwiftUI app, you need to add the following to your
AppDelegate
:That lets the SwiftUI app know about your
SceneDelegate
class.Those changes, combined with your existing setup in Info.plist for the shortcut items and the URL scheme queries, will allow your app to respond to the shortcut items as expected.