Как я могу открыть приложение SwiftUI и выполнить функцию с помощью NSUseractivity из расширения Siri Intent Extension?

Моя проблема

В моем приложении есть собственные действия с ярлыками, созданные с использованием расширений Intents. Они отлично выполняют фоновые действия.

Для некоторых действий я пытаюсь заставить расширение намерения открывать основное (контейнерное) приложение при запуске в ярлыках и выполнять функцию.

У меня проблемы с NSUserActivity, и я не уверен, что это проект SwiftUI или то, как я его реализую (или и то, и другое).

Что я пробовал

Я зарегистрировал свое имя NSUserActivity как NSUserActivityType в моем info.plist ("com.me.project.activityName").

Я добавил приведенный ниже код в свой AppDelegate.

Я инициализирую новый NSUserActivity внутри своего расширения намерения с тем же типом, что и тот, который объявлен в info.plist.

Я также пробовал объявить активность в приложении (не думаю, что мне это нужно?)

Я использую: iOS (iPhone XS, Beta 6), macOS 10.15 Catalina (Beta 5) Xcode 11.0 (Beta 5)

Мой код до сих пор

У меня это в моем AppDelegate:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {

          if userActivity.activityType == "com.me.project.activityName" {
              if let url = URL(string: "https://www.google.com") {
                  UIApplication.shared.open(url)
              }
              return true
          }
          return false
      }

Это входит в мое намерение расширения:

let openApp = intent.launchApp?.boolValue ?? false
        if openApp {

            let UA = NSUserActivity(activityType: "com.me.project.activityName")
            UA.title = "Dummy title"

            completion(UserActivityTestIntentResponse(code: .continueInApp, userActivity: UA))

        } else {
            completion(UserActivityTestIntentResponse.success(result: "You chose not to open the app with a user activity."))
        }

В info.plist

<key>NSUserActivityTypes</key>
    <array>
        <string>com.me.project.activityName</string>
    </array>

У меня это объявлено в быстром файле в моем проекте (хотя я не думаю, что мне это нужно):

let openURLActivityType = "com.me.project.activityName"

let viewPageActivity: NSUserActivity = {
    let userActivity = NSUserActivity(activityType: openURLActivityType)
    userActivity.title = "Dummy Title"
    userActivity.suggestedInvocationPhrase = "Dummy phrase"
    userActivity.isEligibleForSearch = false
    userActivity.isEligibleForPrediction = false
    return userActivity
}()

Неожиданные результаты

Я ожидаю, что при выполнении действия в ярлыках мое приложение откроется и веб-сайт "https://www.google.com ".

В настоящее время после запуска действия в ярлыках мое приложение запускается на главный экран, и больше ничего не происходит. Похоже, что в моем appDelegate нет точек останова.

Я не могу понять, потому что я неправильно использую NSUserActivity, будь то SwiftUI или что-то, что просто не работает изнутри намерения.

Заранее благодарим вас за любую помощь!


person mralexhay    schedule 15.08.2019    source источник
comment
У меня такая же проблема. Что касается меня, то я просто использую NSUserActivity и пытаюсь реализовать функцию передачи обслуживания и поиска в центре внимания (isEligibleForSearch). Для меня тоже кажется, что в моем appDelegate нет точек останова. Я использую macOS Catalina 10.15 beta 7, iOS 13 beta 8 и iPhone X с Xcode 11 beta 7   -  person L. Stephan    schedule 04.09.2019
comment
У меня это в основном работает, добавьте это к моему делегату сцены: func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { if let intent = userActivity.interaction?.intent as? YourIntent { print(“do something”) } } Однако он не срабатывает, если приложение закрыто для начала, поэтому я все еще пытаюсь запустить делегат приложения.   -  person mralexhay    schedule 07.09.2019
comment
Спасибо, это мне очень помогло. Я понял, как можно продолжить UserActivity, когда приложение закрыто. Посмотрите мой ответ ниже   -  person L. Stephan    schedule 07.09.2019


Ответы (2)


Как вы уже прокомментировали, вы можете использовать func scene(_ scene: UIScene, continue userActivity: NSUserActivity), чтобы продолжить NSUserActivity, когда приложение все еще находится в фоновом режиме.

Однако, когда приложение закрыто, вы можете реализовать let userActvity = connectionOptions.userActivities.first внутри метода scene(_:willConnectTo:options:) внутри SceneDelegate, чтобы получить доступ к активности, которую пользователь хочет продолжить.

Простое добавление этого фрагмента кода в стандартную реализацию этого метода будет выглядеть так:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Use a UIHostingController as window root view controller
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: ContentView())
        self.window = window
        window.makeKeyAndVisible()

        //Standard implementation until here
        if let userActvity = connectionOptions.userActivities.first {
            if let intent = userActivity.interaction?.intent as? YourIntent {
                print(“do something”)
            }
        }
    }
}

Это работает для намерений Siri с Swift 5 Xcode 11.

Для Handoff, хотя .userActivities не следует использовать, как говорится в документации, вместо этого будут вызываться связанные методы делегата (scene(_:continue:)):

Это свойство не содержит объектов пользовательской активности, связанных с Handoff. Во время соединения UIKit предоставляет только тип взаимодействия Handoff в свойстве handoffUserActivityType. Позже он вызывает дополнительные методы делегата для доставки самого объекта NSUserActivity.

person L. Stephan    schedule 07.09.2019
comment
Забавно, я понял это и сегодня днем. Спасибо за вашу помощь! - person mralexhay; 07.09.2019
comment
У меня не совсем работает. Последний кусок должен быть if let _ = connectionOptions.userActivities.first?.interaction?.intent as? YourIntent { //your code } - person leonboe1; 12.11.2020
comment
то, что вы предлагаете сделать в одном операторе if, в моем ответе разделено на два - person L. Stephan; 14.11.2020

Если ваше приложение является приложением SwiftUI, возможно, вам стоит подумать о добавлении чего-то вроде:

ContentView
   .onContinueUserActivity("com.company.app.activityIdentifier") { userActivity in 
      handleShortcut(with: userActivity)
   }

где handleShortcut получает userInfos из заданного NSUserActivity и запускает действия, которые должны быть выполнены при открытии вашего приложения.

Это заменяет любые реализации, сделанные в методе делегата func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool, который больше не вызывается в вашем приложении SwiftUI, если вы не сделаете еще несколько (и не тривиальных) строк кода, чтобы вызвать его

Вы можете найти более подробную информацию о:

person jgodon    schedule 24.03.2021
comment
Спасибо за Ваш ответ. Первоначальный вопрос был задан до того, как был анонсирован протокол приложений SwiftUI, но с тех пор я делаю это именно так. - person mralexhay; 25.03.2021