How to add home screen quick action in SwiftUI

Issue #774

Start by defining your quick actions. You can use UIApplicationShortcutIcon(type:) for predefined icons, or use UIApplicationShortcutIcon(systemImageName:) for SFSymbol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum QuickAction: String {
case readPasteboard
case clear

var shortcutItem: UIApplicationShortcutItem {
switch self {
case .readPasteboard:
return UIApplicationShortcutItem(
type: rawValue,
localizedTitle: "Read Pasteboard",
localizedSubtitle: "",
icon: UIApplicationShortcutIcon(type: .add),
userInfo: nil
)
case .clear:
return UIApplicationShortcutItem(
type: rawValue,
localizedTitle: "Clear Pasteboard",
localizedSubtitle: "",
icon: UIApplicationShortcutIcon(systemImageName: SFSymbol.wind.rawValue),
userInfo: nil
)
}
}
}

Add a service to store selected quick action. I usually make this conform to ObservableObject to be able to bind to SwiftUI views later

1
2
final class QuickActionService: ObservableObject {
var shortcutItem: UIApplicationShortcutItem?

Expose AppDelegate and SceneDelegate to your SwiftUI App. Listen to scenePhase to add dynamic items

From Define Dynamic Quick Actions

Set dynamic screen quick actions at any point, but the sample sets them in the sceneWillResignActive(_:) function of the scene delegate. During the transition to a background state is a good time to update any dynamic quick actions, because the system executes this code before the user returns to the Home Screen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@main
struct PastePaliOSApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self)
var appDelegate
@Environment(\.scenePhase)
var scenePhase

var body: some Scene {
WindowGroup {
main
}
.onChange(of: scenePhase) { scenePhase in
switch scenePhase {
case .background:
addDynamicQuickActions()
case .active:
QuickActionService.shared.perform()
default:
break
}
}
}

private func addDynamicQuickActions() {
UIApplication.shared.shortcutItems = [
QuickAction.readPasteboard.shortcutItem,
QuickAction.clear.shortcutItem
]
}
}

Quick actions are notified in 2 cases

  • If the app isn’t already loaded, it’s launched and passes details of the shortcut item in through the connectionOptions parameter of the scene(_:willConnectTo:options:) function in AppDelegate
  • If your app is already loaded, the system calls the windowScene(_:performActionFor:completionHandler:) function of your SceneDelegate

Therefore we need to handle both cases.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
if let shortcutItem = options.shortcutItem {
QuickActionService.shared.shortcutItem = shortcutItem
}

let sceneConfiguration = UISceneConfiguration(
name: "Default",
sessionRole: connectingSceneSession.role
)
sceneConfiguration.delegateClass = SceneDelegate.self

return sceneConfiguration
}
}

private final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func windowScene(
_ windowScene: UIWindowScene,
performActionFor shortcutItem: UIApplicationShortcutItem,
completionHandler: @escaping (Bool) -> Void
) {
QuickActionService.shared.shortcutItem = shortcutItem
completionHandler(true)
}

Read more

For more please consult official Apple docs and design


Updated at 2021-02-10 06:43:19

Comments