iOS快捷方式循环跳转问题:后台应用无需重复唤起
解决iOS快捷方式循环跳转问题
问题背景
我实现了一个iOS快捷方式,预期是打开目标应用后唤起我的应用,但现在出现循环跳转问题:
当前实际流程
- 打开目标应用
- 唤起我的应用
- 再次打开已处于后台的目标应用
- 再次唤起我的应用
期望流程
- 打开目标应用
- 唤起我的应用
- 再次打开已处于后台的目标应用
- 目标应用已在后台,不再唤起我的应用
当前实现代码
import AppIntents import SwiftUI import SwiftUICore import UIKit // 快捷方式中导航选项的枚举 enum NavigationType: String, AppEnum, CaseDisplayRepresentable { case library case book @available(iOSApplicationExtension 16, *) static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Navigation") @available(iOSApplicationExtension 16.0, *) static var caseDisplayRepresentations: [Self:DisplayRepresentation] = [ .library: DisplayRepresentation(title: "Library", subtitle: "Return to the home page", image: .init(systemName: "books.vertical")), .book: DisplayRepresentation(title: "Book", subtitle: "Navigate to a specific book", image: .init(systemName: "book")) ] } // 目标应用选项枚举 enum AppOption: String, AppEnum, CaseDisplayRepresentable { case youtube = "YouTube" case facebook = "Facebook" case twitter = "Twitter" @available(iOSApplicationExtension 16.0, *) static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "App") @available(iOSApplicationExtension 16.0, *) static var caseDisplayRepresentations: [Self: DisplayRepresentation] = [ .youtube: DisplayRepresentation(title: "YouTube", subtitle: "com.google.ios.youtube"), .facebook: DisplayRepresentation(title: "Facebook", subtitle: "com.facebook.ios"), .twitter: DisplayRepresentation(title: "Twitter", subtitle: "com.twitter.ios") ] } // 应用信息结构体 struct App { let appName: String let bundleID: String let deepLinking:String } // 应用数据列表 let appsDetail = [ App(appName: "YouTube", bundleID: "com.google.ios.youtube",deepLinking:"youtube://"), App(appName: "Facebook", bundleID: "com.facebook.ios",deepLinking:"fb://"), App(appName: "Twitter", bundleID: "com.twitter.ios",deepLinking:"twitter://") ] @available(iOSApplicationExtension 16.0, *) struct OpenBook: AppIntent { @available(iOSApplicationExtension 16, *) static var title: LocalizedStringResource = "Open App" @available(iOSApplicationExtension 16.0, *) static var description: IntentDescription = IntentDescription("This action will open the selected book in the Booky app or navigate to the home library.", categoryName: "Navigation") static var openAppWhenRun = true @Parameter(title: "App", description: "The book to open in Booky", requestValueDialog: IntentDialog("Which book would you like to open?")) var apps: AppOption @Parameter(title: "Navigation", description: "Choose whether to open a book or navigate to Booky's library", default: .book, requestValueDialog: IntentDialog("What would you like to navigate to?")) var navigation: NavigationType static var parameterSummary: some ParameterSummary { Summary("Open \(\.$apps)") } @MainActor func perform() async throws -> some IntentResult { guard let selectedBook = appsDetail.first(where: { $0.appName == apps.rawValue }) else { throw NSError(domain: "OpenBookError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Book not found."]) } let userInfo: [String: Any] = ["selectedBook": [ "appName": selectedBook.appName, "bundleId": selectedBook.bundleID, "deepLinking": selectedBook.deepLinking, ]] NotificationCenter.default.post(name: NSNotification.Name("OpenBookCallback"), object: nil, userInfo: userInfo) return .result() } }
解决方案
循环跳转的核心原因是每次打开目标应用都会触发快捷方式的唤起逻辑,不管应用是冷启动还是后台唤醒。解决关键是区分目标应用的启动类型,仅在首次冷启动时触发你的应用唤起。
具体实现步骤
- 添加启动状态标记
在快捷方式的Intent逻辑中,通过UserDefaults记录目标应用的启动来源:
@MainActor func perform() async throws -> some IntentResult { guard let selectedApp = appsDetail.first(where: { $0.appName == apps.rawValue }) else { throw NSError(domain: "OpenBookError", code: 1, userInfo: [NSLocalizedDescriptionKey: "应用未找到。"]) } // 标记当前是通过快捷方式启动目标应用 UserDefaults.standard.set(true, forKey: "LaunchedFromShortcut_\(selectedApp.bundleID)") let userInfo: [String: Any] = ["selectedBook": [ "appName": selectedApp.appName, "bundleId": selectedApp.bundleID, "deepLinking": selectedApp.deepLinking, ]] NotificationCenter.default.post(name: NSNotification.Name("OpenBookCallback"), object: nil, userInfo: userInfo) return .result() }
- 在目标应用中处理状态
如果你能控制目标应用的代码,在启动回调中添加判断逻辑:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let bundleID = Bundle.main.bundleIdentifier ?? "" let launchedFromShortcut = UserDefaults.standard.bool(forKey: "LaunchedFromShortcut_\(bundleID)") if launchedFromShortcut { // 唤起你的应用(替换为你的应用Deep Link) if let url = URL(string: "yourapp://") { application.open(url) } // 重置标记,避免后台唤起重复触发 UserDefaults.standard.set(false, forKey: "LaunchedFromShortcut_\(bundleID)") } return true } // 后台唤起时重置标记 func applicationDidBecomeActive(_ application: UIApplication) { let bundleID = Bundle.main.bundleIdentifier ?? "" UserDefaults.standard.set(false, forKey: "LaunchedFromShortcut_\(bundleID)") }
- 无法控制目标应用时的替代方案
在你的应用中监听目标应用的状态:
- 当你的应用被唤起时,通过
UIApplication.shared.applicationState判断目标应用是否在后台 - 监听
UIApplication.didBecomeActiveNotification,在回调中跳过后台唤起场景的触发逻辑
核心思路
通过状态标记区分目标应用的首次冷启动和后台唤起,仅在首次启动时执行你的应用唤起逻辑,后台唤起时跳过,从根源避免循环跳转。
内容的提问来源于stack exchange,提问作者ketan patel
相关产品推荐
相关产品推荐

