AddToSiriButton

AddToSiriButton 提供了一个 UIKit 添加到 Siri 按钮的包装器,以便它可以在 SwiftUI 项目中使用。

支持

如果您觉得 AddToSiriButton 有用,并希望帮助支持其持续开发和维护,请考虑进行小额捐赠,尤其是在您将其用于商业产品的情况下。

Buy Me A Coffee

正是通过像您这样的贡献者的支持,我才能继续构建、发布和维护像 AddToSiriButton 这样高质量、文档齐全的 Swift 包,并免费提供。

安装

Swift Package Manager (Xcode 11 及以上版本)

  1. 在 Xcode 中,选择 文件 > Add Package Dependency… 菜单项。
  2. 在对话框中粘贴 https://github.com/Appracatappra/AddToSiriButton.git
  3. 按照 Xcode 的指示完成安装。

为什么不用 CocoaPods、Carthage 或其他方式?

支持多个依赖管理器会使维护库的工作呈指数级增长,变得更加复杂和耗时。

由于 Swift Package Manager 与 Xcode 11(及更高版本)集成,因此它是进一步支持的最简单选择。

使用 AddToSiriButton

AddToSiriButton 在 SwiftUI View 中非常容易使用

import SwiftUI
import AddToSiriButton

struct StoreProductList: View {

	var isSiriEnabled:Bool {
        #if os(iOS)
        let status = INPreferences.siriAuthorizationStatus()
        
        switch status {
        case .notDetermined:
            return false
        case .restricted:
            return false
        case .denied:
            return false
        case .authorized:
            return true
        @unknown default:
            return false
        }
        #else
        return false
        #endif
    }

	var body: some View {
        VStack {
        	#if os(iOS)
	        if isSiriEnabled {
	            VStack {
	                if let voiceShortcut = ShortcutManager.shared.getAddItemVoiceShortcut(for: Store.tableName) {
	                    SiriButtonView(voiceShortcut: voiceShortcut)
	                } else {
	                    SiriButtonView(intent: ShortcutManager.shared.getIntent(for: .addItemIntent, storeName: store.name))
	                }
	            }
	            .frame(height: 50.0)
	        }
	        #endif
        }
    }
}

如果用户尚未添加快捷方式,则会在 iOS 上显示以下按钮

或者,如果用户已经创建了快捷方式,则会在 iOS 上显示此按钮

给定以下辅助文件

//
//  ShortcutManager.swift
//  Stuff To Get (iOS)
//
//  Created by Kevin Mullins on 6/24/21.
//  From: https://www.reddit.com/r/SwiftUI/comments/fhh1fw/add_to_siri_button_with_swiftui/
//

import UIKit
import Intents

/**
 Class to manage Siri Voice Shortcuts for the your app. It provides function to load all shortcuts for your app, test to see if a shortcut has already been created and to donate a shortcut to Siri, Shortcuts and Spotlight Search.
 */
@available(iOS 12.0, *)
public final class ShortcutManager {
    
    // MARK: Static Properties
    /// A shared shortcut manager.
    public static let shared = ShortcutManager()
    
    // MARK: Properties
    /// The list of all shortcuts defined for this app.
    var voiceShortcuts:[INVoiceShortcut] = []
    
    // MARK: Initializers
    
    /// Creates a new instant of the object.
    public init() {
        // Initialize
        self.loadAllShortcuts()
    }
    
    // MARK: Functions
    
    /// Loads all predefined shortcuts for the app.
    func loadAllShortcuts() {
        INVoiceShortcutCenter.shared.getAllVoiceShortcuts  { (voiceShortcutsFromCenter, error) in
            guard let voiceShortcutsFromCenter = voiceShortcutsFromCenter else {
                print("Faild to fetch all voice shortcuts: \(String(describing: error))")
                return
            }
            self.voiceShortcuts = voiceShortcutsFromCenter
        }
    }
    
    
    /// Donates the given intent to Siri and Spotlight Search.
    /// - Parameters:
    ///   - intent: The intent to donate.
    ///   - id: The Group ID of the intent to donate.
    func donate(_ intent: INIntent, id: String? = nil) {
        // create a Siri interaction from our intent
        let interaction = INInteraction(intent: intent, response: nil)
        if let id = id {
            interaction.groupIdentifier = id
        }
        
        // donate it to the system
        interaction.donate { error in
            // if there was an error, print it out
            if let error = error {
                print(error)
            }
        }
        
        if let shortcut = INShortcut(intent: intent) {
            let relevantShortcut = INRelevantShortcut(shortcut: shortcut)
            INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in
                if let error = error {
                    print("Error setting relevant shortcuts: \(error)")
                }
            }
        }
    }
    
    /// Tests to see if the given store already has an **Add Item** shortcut definted for it.
    /// - Parameter store: The store to check for.
    /// - Returns: `true` if the store already has a shortcut, else returns `false`.
    public func hasAddItemIntent(for store:String) -> Bool {
        
        // Search all voice shortcuts
        for voiceShorcut in voiceShortcuts {
            if let intent = voiceShorcut.shortcut.intent as? AddItemIntent {
                if intent.store == store {
                    // Found
                    return true
                }
            }
        }
        
        // Not found
        return false
    }
    
    /// Returns the `INVoiceShortcut` for the given store name if a shortcut has already been defined or returns `nil`.
    /// - Parameter store: The name of the store to search for.
    /// - Returns: The requested `INVoiceShortcut` or `nil` if not found.
    public func getAddItemVoiceShortcut(for store:String) -> INVoiceShortcut? {
        
        // Search all voice shortcuts
        for voiceShorcut in voiceShortcuts {
            if let intent = voiceShorcut.shortcut.intent as? AddItemIntent {
                if intent.store == store {
                    // Found
                    return voiceShorcut
                }
            }
        }
        
        // Not found
        return nil
    }
    
    /// Generates an intent of the requested type with the given optional properties.
    /// - Parameters:
    ///   - shortcut: The type of shortcut intent to generate.
    ///   - storeName: The optional name of the store to generate the intent for.
    ///   - productName: The optional name of the product to generate the intent for.
    ///   - quantity: The optional quantity to generate the intent for.
    /// - Returns: The requested intent or `nil` if the intent cannot be generated.
    public func getIntent(for shortcut:Shortcut, storeName:String = "", productName:String = "", quantity:Int = 1) -> INIntent? {
        
        switch shortcut {
        case .addItemIntent:
            let intent = shortcut.intent as! AddItemIntent
            if !storeName.isEmpty {
                intent.suggestedInvocationPhrase = "Add Item To \(storeName)"
                intent.store = storeName
            }
            if !productName.isEmpty {
                intent.product = productName
            }
            if quantity > 0 {
                intent.quantity = NSNumber.init(value: quantity)
            }
            return intent
        default:
            return nil
        }
    }
    
    // MARK: - Enumerations
    /**
     This enum defines the different types on intent that our app can generate along with properties defining them.
     */
    public enum Shortcut {
        /// The **Add Item** intent.
        case addItemIntent
        
        /// Returns the unique ID of the intent.
        var intentId: String {
            switch self {
            case .addItemIntent: return "addItemIntentShortcut"
            }
        }
        
        /// Returns the `INIntent` for the given type.
        var intent: INIntent {
            var intent: INIntent
            switch self {
            case .addItemIntent:
                intent = AddItemIntent()
            }
            intent.suggestedInvocationPhrase = suggestedInvocationPhrase
            return intent
        }
        
        /// Returns the suggested invocation phrase for the given type.
        var suggestedInvocationPhrase: String {
            switch self {
            case .addItemIntent: return "Add Item"
            }
        }
        
        /// Returns the short title for the given type.
        var shortTitle: String {
            switch self {
            case .addItemIntent: return "Add Item"
            }
        }
        
        /// Donates the given type of Intent to Siri and Spotlight Search.
        func donate() {
            // create a Siri interaction from our intent
            let interaction = INInteraction(intent: self.intent, response: nil)
            interaction.groupIdentifier = intentId
            
            // donate it to the system
            interaction.donate { error in
                // if there was an error, print it out
                if let error = error {
                    print(error)
                }
            }
            
            
            if let shortcut = INShortcut(intent: intent) {
                let relevantShortcut = INRelevantShortcut(shortcut: shortcut)
                INRelevantShortcutStore.default.setRelevantShortcuts([relevantShortcut]) { error in
                    if let error = error {
                        print("Error setting relevant shortcuts: \(error)")
                    }
                }
            }
            
        }
    }
}

注意:AddItemIntent 是一个 Custom Intent,已添加到上面代码中的 Swift 项目中,如何创建和实现意图超出了本文档的范围。请参阅 Apple 的文档以获取帮助。

文档

软件包 包含所有功能的完整 DocC 文档