web-dev-qa-db-ja.com

siriショートカットボタン(INUIAddVoiceShortcutButton)は、複数のショートカットがある場合に間違ったタイトルを表示します(NSUserActivity)

アプリに2つのsiriショートカットがあります。 NSUserActivityを使用してこれらのショートカットを寄付します。また、info.plistに2つのNSUserActivityTypesを作成しました。

これらのショートカットを処理する2つのビューコントローラーがあります(1つのショートカットに対して1つのビューコントローラー)。

1つのViewControllerから1つのsiriショートカットを追加してから、2番目のView Controllerに移動すると、2番目のView ControllerのネイティブのSiriショートカットボタン(INUIAddVoiceShortcutButton)が自動的に最初のショートカット(1番目のView Controllerから作成)を選択し、「追加されました「Siriに追加」ボタンを表示する代わりに、提案されたフレーズで「Siriに」を表示します。 NSUserActivityごとに異なる識別子があることを再確認しましたが、それでもどういうわけか間違ったショートカットを選択します。

ビューコントローラー1:

let userActivity = NSUserActivity(activityType: "com.activity.type1")
userActivity.isEligibleForSearch = true
userActivity.isEligibleForPrediction = true
userActivity.title = shortcut.title
userActivity.suggestedInvocationPhrase = suggestedPhrase

let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity.contentAttributeSet = attributes
let shortcut = INShortcut(userActivity: userActivity)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)

ビューコントローラー2:

let userActivity2 = NSUserActivity(activityType: "com.activity.type2")
userActivity2.isEligibleForSearch = true
userActivity2.isEligibleForPrediction = true
userActivity2.title = shortcut.title
userActivity2.suggestedInvocationPhrase = suggestedPhrase

let attributes = CSSearchableItemAttributeSet(itemContentType: kUTTypeItem as String)
attributes.contentDescription = description
userActivity2.contentAttributeSet = attributes

let shortcut = INShortcut(userActivity: userActivity2)
let siriButton = INUIAddVoiceShortcutButton(style: .whiteOutline)
siriButton.translatesAutoresizingMaskIntoConstraints = false
siriButton.shortcut = shortcut
self.view.addSubview(siriButton)

電話の設定アプリからショートカットを削除せずにアプリを削除して再インストールすると、同様のことが起こります。

8
Bilal

IOSバグのようです。この問題の回避策を見つけました。ユーザーがsiriショートカットを追加/編集するたびに、新しいsiriボタンを作成する必要があります。siriボタンを作成する前に、次のこと

1-関数を呼び出して、INVoiceShortcutCenterからすべての音声ショートカットを取得します。これは非同期で発生するため、データが必要になる前に(AppDelegateなどで)実行する必要があることに注意してください。また、ユーザーがSiriショートカット(おそらくINUIAddVoiceShortcutViewControllerDelegate.addVoiceShortcutViewController(_:didFinishWith:error)メソッド内)を追加するたびに、これを再ロードする必要があります。

INVoiceShortcutCenter.shared.getAllVoiceShortcuts  { (voiceShortcutsFromCenter, error) in
    guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
            if let error = error as NSError? {
                os_log("Failed to fetch voice shortcuts with error: %@", log: OSLog.default, type: .error, error)
            }
            return
        }
        self.voiceShortcuts = voiceShortcutsFromCenter
}

2- View Controller-1で、すべての音声ショートカットを繰り返して、ショートカットがすでに追加されているかどうかを確認します

let voiceShorcut = voiceShortcuts.first { (voiceShortcut) -> Bool in
    if let activity = voiceShortcut.shortcut.userActivity, activity.activityType == "com.activity.type1" {
        return true
    }
    return false
}

3-音声ショートカットが登録されている場合は、INShortcutをsiriボタンに渡します。それ以外の場合は、設定しないでください。

if voiceShorcut != nil {
    let shortcut = INShortcut(userActivity: userActivity1)
    siriButton.shortcut = shortcut
} 

Second ViewControllerでも同じことを行います。

3
Bilal

IOS12.0のバグです。 INUIAddVoiceShortcutButton.voiceShortcutを正しい値に更新することで修正できます。 KVOを使用して「voiceShortcut」プロパティを監視し、変更されたら正しい値を割り当てます。

3

したがって、デフォルトのSiriボタンを使用することはできません。カスタムUIButtonを使用する必要があります。クラスVoiceShortcutsManagerはすべての音声インテントをチェックし、そのリストを検索して、一致するものが1つあるかどうかをチェックします。したがって、追加を提案する必要がない場合は、エディションを提案する必要があります。

public class VoiceShortcutsManager {

    private var voiceShortcuts: [INVoiceShortcut] = []

    public init() {

        updateVoiceShortcuts(completion: nil)
    }


    public func voiceShortcut(for order: DeviceIntent, powerState: State) -> INVoiceShortcut? {

        for element in voiceShortcuts {

            guard let intent = element.shortcut.intent as? ToggleStateIntent else {
                continue
            }
            let deviceIntent = DeviceIntent(identifier: intent.device?.identifier, display: intent.device?.displayString ?? "")
            if(order == deviceIntent && powerState == intent.state) {
                return element
            }
        }
        return nil
    }


    public func updateVoiceShortcuts(completion: (() -> Void)?) {

        INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (voiceShortcutsFromCenter, error) in
            guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
                if let error = error {
                    print("Failed to fetch voice shortcuts with error: \(error.localizedDescription)")
                }
                return
            }
            self.voiceShortcuts = voiceShortcutsFromCenter
            if let completion = completion {
                completion()
            }
        }
    }

}

そして、ViewControllerに実装します

class SiriAddViewController: ViewController {

    let voiceShortcutManager = VoiceShortcutsManager.init()

    override func viewDidLoad() {
        super.viewDidLoad()

        contentView.btnTest.addTarget(self, action: #selector(self.testBtn), for: .touchUpInside)
    }

    ...


    @objc func testBtn() {

        let deviceIntent = DeviceIntent(identifier: smartPlug.deviceID, display: smartPlug.alias)

        //is action already has a shortcut, update shortcut else create shortcut
        if let shortcut = voiceShortcutManager.voiceShortcut(for: deviceIntent, powerState: .off) {

            let editVoiceShortcutViewController = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut)
            editVoiceShortcutViewController.delegate = self
            present(editVoiceShortcutViewController, animated: true, completion: nil)
        } else if let shortcut = INShortcut(intent: intentTurnOff) {

            let addVoiceShortcutVC = INUIAddVoiceShortcutViewController(shortcut: shortcut)
            addVoiceShortcutVC.delegate = self
            present(addVoiceShortcutVC, animated: true, completion: nil)
        }
    }

}


@available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutButtonDelegate {


    func present(_ addVoiceShortcutViewController: INUIAddVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {
        addVoiceShortcutViewController.delegate = self
        addVoiceShortcutViewController.modalPresentationStyle = .formSheet
        present(addVoiceShortcutViewController, animated: true, completion: nil)
    }


    func present(_ editVoiceShortcutViewController: INUIEditVoiceShortcutViewController, for addVoiceShortcutButton: INUIAddVoiceShortcutButton) {

        editVoiceShortcutViewController.delegate = self
        editVoiceShortcutViewController.modalPresentationStyle = .formSheet
        present(editVoiceShortcutViewController, animated: true, completion: nil)
    }

}


@available(iOS 12.0, *)
extension SiriAddViewController: INUIAddVoiceShortcutViewControllerDelegate {

    func addVoiceShortcutViewController(_ controller: INUIAddVoiceShortcutViewController, didFinishWith voiceShortcut: INVoiceShortcut?, error: Error?) {

        voiceShortcutManager.updateVoiceShortcuts(completion: nil)
        controller.dismiss(animated: true, completion: nil)
    }

    func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) {
        controller.dismiss(animated: true, completion: nil)
    }

}


@available(iOS 12.0, *)
extension SiriAddViewController: INUIEditVoiceShortcutViewControllerDelegate {


    func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didUpdate voiceShortcut: INVoiceShortcut?, error: Error?) {

        voiceShortcutManager.updateVoiceShortcuts(completion: nil)
        controller.dismiss(animated: true, completion: nil)
    }

    func editVoiceShortcutViewController(_ controller: INUIEditVoiceShortcutViewController, didDeleteVoiceShortcutWithIdentifier deletedVoiceShortcutIdentifier: UUID) {

        voiceShortcutManager.updateVoiceShortcuts(completion: nil)
        controller.dismiss(animated: true, completion: nil)
    }

    func editVoiceShortcutViewControllerDidCancel(_ controller: INUIEditVoiceShortcutViewController) {

        voiceShortcutManager.updateVoiceShortcuts(completion: nil)
        controller.dismiss(animated: true, completion: nil)
    }

}
}

このコードは、このWebページからインスピレーション/コピーされています: https://www.nodesagency.com/test-drive-a-siri-shortcuts-intro/

0
Tiago Mendes

インテントの設定に移動しましたが、インテントの設定が1つだけで、INUIAddVoiceShortcutButtonを使用しても、ショートカットを追跡できないことがわかりました。フレーズが録音されると、Additioned to Siri withphraseが表示されます。

ただし、アプリを再起動するたびに、[Add to Siri]ボタンの代わりに、[Add to Siri]]ボタンが表示され、フレーズが録音されます。

Bilalの提案で試してみましたが、INVoiceShortcutCenterにショートカットが表示されているのがわかりますが、Siriボタンにロードされていません。

ボタン自体のコードは次のようになります。

 private func addSiriButton() {
    let addShortcutButton = INUIAddVoiceShortcutButton(style: .blackOutline)
    addShortcutButton.delegate = self

    addShortcutButton.shortcut = INShortcut(intent: engine.intent )
    addShortcutButton.translatesAutoresizingMaskIntoConstraints = false

    siriButtonSubView.addSubview(addShortcutButton)
    siriButtonSubView.centerXAnchor.constraint(equalTo: addShortcutButton.centerXAnchor).isActive = true
    siriButtonSubView.centerYAnchor.constraint(equalTo: addShortcutButton.centerYAnchor).isActive = true

}

私はすべてのプロトコルを実装していて、Soupアプリを詳しく調べましたが、この不正確さの原因を理解できません。

面白いことに、British Airwaysのアプリ開発者でさえ、ボタンの障害動作がまったく同じであるため、それをあきらめています。

更新:インテントと追加先Siriおよび追加先Siri)の最小限の実装で別のテストプロジェクトを構築しましたこの時点で、私自身のアプリのコードベースに、この望ましくない動作を引き起こしている何かがあると推測しています。

更新2問題を修正したことをみんなに知らせたかっただけです。インテントの使用は問題なく機能しますが、インテント定義ファイル自体には確かに少しの感度があります。私がしなければならなかったのは、新しいインテントを作成することだけで、それが生成され、それが機能しました。私の最初の意図はどういうわけか壊れていたようですが、エラーはありませんでした。別のインテントを作成し、インテント処理関数をそれに再割り当てした後、すべてが意図したとおりに機能しました。 (しゃれを意図した)

0
Dan Korkelia

私は自分の実装(元々はsoupchefアプリに基づいています)をApple( https://developer.Apple.com/documentation/sirikit/inuiaddvoiceshortcutbutton ):

編集:UserActivityとカスタムIntentショートカットの両方のshortcutObject(INShortcut)を作成して渡す方法を示すコードを追加しました。

ショートカットクラスは、カスタムインテントのインスタンス化を返すインテントと呼ばれる計算されたプロパティを含む列挙型です。

private func addShortcutButton(shortcut: Shortcut, parentViewController: UIViewController, shortcutViewControllerDelegate: INUIAddVoiceShortcutViewControllerDelegate) {
    guard let view = parentViewController.view else { return }

    if let intent = shortcut.intent {
        shortcutObject = INShortcut(intent: intent)
    } else if let userActivity = view.userActivity {
        shortcutObject = INShortcut(userActivity: userActivity)
    }

    self.shortcutViewControllerDelegate = shortcutViewControllerDelegate
    addSiriButton(to: shortcutButtonContainer)
}

func addSiriButton(to view: UIView) {
    let button = INUIAddVoiceShortcutButton(style: .whiteOutline)
    button.translatesAutoresizingMaskIntoConstraints = false

    view.addSubview(button)
    view.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
    view.centerYAnchor.constraint(equalTo: button.centerYAnchor).isActive = true

    button.addTarget(self, action: #selector(addToSiri(_:)), for: .touchUpInside)
}

// Present the Add Shortcut view controller after the
// user taps the "Add to Siri" button.
@objc
func addToSiri(_ sender: Any) {
    guard let shortcutObject = shortcutObject else { return }
    let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcutObject)
    viewController.modalPresentationStyle = .formSheet
    viewController.delegate = shortcutViewControllerDelegate
    parentViewController?.present(viewController, animated: true, completion: nil)
}
0
rmooney