美文网首页
iOS Siri Shortcuts 快捷指令添加(Flutte

iOS Siri Shortcuts 快捷指令添加(Flutte

作者: Sonoface | 来源:发表于2024-03-27 14:49 被阅读0次

    最近公司的项目中需要加入Siri 快捷方式的功能,引入过程中遇到很多问题特此记录。

    需求

    在系统应用“快捷指令”中添加指令,无须打开原App即可调用运行功能。


    Flutter 部分

    我的是Flutter工程,native同学可以直接跳过这部分哈

    主要是使用methodChannel做通信转发事件,安卓和iOS需要各自声明。
    以下文为例:

      Future<Map> methodChannelGainShortcutsList() async {
        ///声明通信频道
        const MethodChannel methodChannel =
            MethodChannel('com.xxxx.xxx.xxxxxx/runner');
        ///声明事件
        Map result = await methodChannel.invokeMethod('gainXXXXXX');
        return result;
      }
    

    iOS需要在AppDelegate中添加:

      override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)
          //meth
          let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
          let channel = FlutterMethodChannel(name: "com.xxxx.xxx.xxxxxx/runner", binaryMessenger: controller.binaryMessenger)
          channel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
              if call.method == "gainXXXXXX" {
                  ///dosthing
                  result(["key":""]);//return内容根据自己需求来
              }
              else {
                  result(FlutterMethodNotImplemented)
              }
          }
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
      }
    

    iOS部分

    接下来开始Siri Shortcuts的项目配置,第一步是添加Siri Kit Intent的配置文件。
    右下角加号->File



    找到SiriKit Intent,Next


    右下角加号,新增一个 Intent

    创建后是这个样子,会自动生成一个Response
    Parameter里可以做很多文章,可以引用多种类型,自建Enum等等

    Command+B build一下,intent会自动生成对应的类文件。

    //
    // IntentIntent.swift
    //
    // This file was automatically generated and should not be edited.
    //
    
    #if canImport(Intents)
    
    import Intents
    
    @available(iOS 12.0, macOS 11.0, watchOS 5.0, *) @available(tvOS, unavailable)
    @objc(IntentIntent)
    public class IntentIntent: INIntent {
    
    }
    
    @available(iOS 12.0, macOS 11.0, watchOS 5.0, *) @available(tvOS, unavailable)
    @objc(IntentIntentHandling)
    public protocol IntentIntentHandling: NSObjectProtocol {
    
        @available(*, renamed: "handle(intent:)")
        @objc(handleIntent:completion:)
        func handle(intent: IntentIntent, completion: @escaping (IntentIntentResponse) -> Swift.Void)
    后面省略...
    
    

    需要注意的是,有时Intents编译缓慢,或者不同Xcode版本下,会有未编译的情况出现。
    如下图右侧中,如果Custom Class的右侧没有出现箭头,则说明文件未编译成功,需要多编译尝试几次。

    成功时,如下图会有小箭头。
    如果希望更改类名,同样是修改这里Custom Class的值,然后编译。


    接下来在General中添加Targets

    搜索intents,并添加

    记住勾选Include UI Extension帮你自动创建对应的UITarget,点击Finish

    注意
    需要将前面创建的Intents.intentdefinition文件在新建的Targets中引入(将红框内勾选中)。

    这个时候编译一下,会出现一个编译循环问题:
    编译器提示:
    Cycle inside Runner; building could produce unreliable results.
    应该是在Flutter中才会出现。是由于编译顺序导致的循环依赖问题。

    解决方法也很简单
    找到主工程 -> Build Phases ->调整顺序
    主要是下图红框内,错序会出现循环依赖问题
    亲测按照本图内顺序调整,即可通过编译

    注意
    如果IntentsSupported中没有对应Intents类名,siriex会在快捷方式中找不到不到对应的处理函数。所以要确保类名已添加,如下图。

    同时,这里会自动生成IntentHandler的类及.h.m文件,IntentHandler是所有Intents的处理函数的入口。
    所以在NSExtensionPrincipalClass中的值也必须保留,更名时必须修改。

    这里的INSendMessageIntent、INSearchForMessagesIntent、INSetMessageAttributeIntent,也会自动生成,如果功能用不上,对应代码也可以删除(本例用不到故删除)

    清理干净后IntentHandler.m文件如下:

    //
    //  IntentHandler.m
    //
    
    #import "IntentHandler.h"
    #import "XXHandler.h"
    #import "XXXHander.h"
    
    @interface IntentHandler ()
    
    @end
    
    @implementation IntentHandler
    
    - (id)handlerForIntent:(INIntent *)intent {
        if([intent isKindOfClass:XXIntent.class]){
            return [[XXIntentHandler alloc]init];
        }
        if([intent isKindOfClass:XXXIntent.class]){
            return [[XXXIntentHander alloc]init];
        }
        return nil;
    }
    
    

    在handlerForIntent中进行类型分发

    而在对应的XXXIntentHandler中,则需要重写handleXXX与confirmXXX两个函数,这里方法名是自动生成的。

    //
    //  XXXIntentHandler.m
    //
    
    #import "XXXIntentHandler.h"
    #import "XXXIntent.h"
    
    @interface XXXIntentHandler()<XXXIntentHandling>
    @end
    @implementation XXXIntentHandler
    
    - (void)handleXXX:(XXXIntent *)intent completion:(void (^)(XXXIntentResponse * _Nonnull))completion{
        completion( [[XXXIntentResponse alloc] initWithCode:XXXIntentResponseCodeSuccess
                                                     userActivity:nil]);
    }
    
    - (void)confirmXXX:(XXXIntent *)intent completion:(void (^)(XXXIntentResponse * _Nonnull))completion{
        completion([[XXXIntentResponse alloc] initWithCode:XXXIntentResponseCodeSuccess
                                                userActivity:nil]);
    }
    
    @end
    
    

    到此就创建完成了,可以进行业务方面的工作。
    快捷方式的UI交互需要在siriui中自定义。

    其他问题

    Bundle Identifier
    你的主工程和siriex、siriexUI的Bundle Identifier必须符合关联递进关系。
    举个例子:
    主工程: com.org.projectname
    siriex: com.org.projectname.siriex
    siriexui: com.org.projectname.siriexui

    如果是修改了Bundle Identifier或者调试工程的时候需要用其他Bundle Identifier也需要修改对应是siriex、siriexui的Bundle Identifier。(App Group同理)

    <NSUserActivity> has an interaction attached but it is not handled
    在过去的iOS版本中是会有报错提示,在更高版本中某些情况也会有这个报错。
    在最初通常是由于找不到handlerForIntent对应实现。
    解决方法也很简单,只要在handlerForIntent中return了对应类并实现了handleXXX方法就行。

    为什么我的Siri Shortcuts 直接打开了应用程序?不在外部执行?
    情况一:
    在某些情况下,会造成这个问题,同样也是游这个NSUserActivity类造成的错误。比如使用了Flutter第三方库 flutter_siri_shortcutssiri_shortcuts
    这两个库提供的方法,是基于以NSUserActivity实现的,所以他们实现的功能是通过siri shortcuts 快速打开应用内部实现,所以使用了这些库会有这个情况出现。

    情况二:
    通常在业务逻辑中我们需要创建INShortcut来

    INShortcut *shortcut = [[INShortcut alloc] initWithIntent:intent];
    INUIAddVoiceShortcutViewController *voiceShortcutVC = [[INUIAddVoiceShortcutViewController alloc] initWithShortcut:shortcut];
    voiceShortcutVC.delegate = self;
    [self presentViewController:voiceShortcutVC animated:YES completion:nil];
    

    这里一定要使用initWithIntent方法创建,如果使用了initWithUserActivity方法创建则会出现这个问题。
    在底层中,Intent、UserActivity应该是继承关系,如果intent没有实现会自动进入UserActivity相关的逻辑。

    情况三:
    由于快捷指令在数据安全、高效、扩展性强个方面的优势。Apple在Siri方面对于Shortcuts功能也进行了多次迭代与扩展,导致了Siri Shortcuts也有着不同版本的支持。

    在Xcode15.3的版本中添加intents会默认Minimum Deployments为最高版本17.4(真是让人相当无语)

    当系统版本低于Minimum Deployments运行该快捷指令时,也会出现直接跳转的问题

    这种情况,很容易在外部分享的快捷指令中出现、在老项目新增Intents扩展的过程中出现、在已有intents扩展但是Xcode升级后出现。

    相关文章

      网友评论

          本文标题:iOS Siri Shortcuts 快捷指令添加(Flutte

          本文链接:https://www.haomeiwen.com/subject/dfqytjtx.html