Day14 开发套件 - 范例程序码介绍03 iOS 端

最後来看Native 端(iOS):

补充:iOS 中的 .h 和.m 档

.h 为标头档,做为宣告属性及方法使用

.m 为Objective-C档,做为实际编写属性值及方法内容使用

这边先介绍一下,Flutter 专案的iOS 入口点在AppDelegate,其继承自Flutter.frameworkFlutterAppDelegate

@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

在启动时进入AppDelegate後会执行GeneratedPluginRegistrant.register(with: self),而GeneratedPluginRegistrant中的(void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry会实现当执行flutter pub get时生成的Flutter 所依赖iOS 端的插件

举例来说,我们来看之前建立plugin 时自动产生的example(使用我们plugin package 的范例专案),此范例专案已经依赖我们的plugin package,我们就可以看到它的GeneratedPluginRegistrant.m

#import "GeneratedPluginRegistrant.h"

#if __has_include(<batterylevel/BatterylevelPlugin.h>)
#import <batterylevel/BatterylevelPlugin.h>
#else
@import batterylevel;
#endif

@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [BatterylevelPlugin registerWithRegistrar:[registry registrarForPlugin:@"BatterylevelPlugin"]];
}

@end

使范例专案在启动iOS 时,会注册Flutter 依赖的iOS 端插件(此时会呼叫我们的pluginBatterylevelPlugin来注册)

回到我们的plugin,我们来看看如何实现Native 端(iOS)的功能:

预设建立的档案主要有:

  • ios/Classes/BatterylevelPlugin

    #import "BatterylevelPlugin.h"
    #if __has_include(<batterylevel/batterylevel-Swift.h>)
    #import <batterylevel/batterylevel-Swift.h>
    #else
    // Support project import fallback if the generated compatibility header
    // is not copied when this plugin is created as a library.
    // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
    #import "batterylevel-Swift.h"
    #endif
    
    @implementation BatterylevelPlugin
    + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
      [SwiftBatterylevelPlugin registerWithRegistrar:registrar];
    }
    @end
    

    当别人使用我们的plugin 时,在启动iOS 後会注册它们Flutter 所依赖的iOS 端插件,我们的plugin 就透过BatterylevelPlugin提供一个SwiftBatterylevelPlugin来注册,其plugin 的功能也会在SwiftBatterylevelPlugin来实现

  • ios/Classes/SwiftBatterylevelPlugin.swift

    用来实现注册方法以及我们插件的功能,其中FlutterPlugin为插件协议,所以必须实作注册方法

    public class SwiftBatterylevelPlugin: NSObject, FlutterPlugin {
      public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "batterylevel", binaryMessenger: registrar.messenger())
        let instance = SwiftBatterylevelPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
      }
    
      public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        result("iOS " + UIDevice.current.systemVersion)
      }
    }
    

    在注册方法中需要新建用来通信的Channel(对应Flutter 端的MethodChannel,iOS 端为FlutterMethodChannel)并与FlutterPluginRegistrarFlutterBinaryMessenger进行连结绑定,再透过调用addMethodCallDelegate方法将Channel注册到FlutterEngine里,这边我们来看看FlutterEngine 里的原始码:

    - (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
                          channel:(FlutterMethodChannel*)channel {
      [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
        [delegate handleMethodCall:call result:result];
      }];
    }
    

    这边将Channel透过setMessageHandlerFlutterBinaryMessenger 注册一个FlutterMethodChannel用的FlutterMethodCallHandler来处理接收讯息,当Flutter 端通过invokeMethod调用Native iOS 端方法时,就会通过传进来的FlutterPlugin 的delegate将讯息回调给FlutterPlugin(这边的SwiftBatterylevelPlugin)的handleMethodCall方法

    回到SwiftBatterylevelPlugin,最後handle用来实现FlutterPlugin- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result,这边产生的范例程序直接回传版本资讯给Flutter 端

    而范例程序并无针对Flutter 端呼叫FlutterMethodCall的不同方法名做判断处理,我们来对其做以下修改:

        public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
            switch call.method {
            case "getPlatformVersion":
                result("iOS " + UIDevice.current.systemVersion)
            default:
                result(FlutterMethodNotImplemented)
            }
        }
    

在看完plugin 范例程序的版本资讯功能後,我们对plugin 对Channel功能的实现已经有初步的了解,这边就先以一样的步骤,新增取得batterylevel 电池电量资讯的功能吧


<<:  @Day29 | C# WixToolset + WPF 帅到不行的安装包 [如何拿已经安装好的资料]

>>:  透过写程序可以更熟悉AWS console上的完整操作

Day26 - GitLab CI 启动其它专案启动流水线或动态产出新的流水线,谈触发 trigger

在大型专案中,可能会把专案依功能、架构等等因素,切分为多个子专案,虽然切分为多个子专案,有些逻辑可能...

Day 24 : Tkinter-利用Python建立GUI(元件篇)

今天会开始来讲元件的部分~ 通用参数 height : 高度 width : 宽度 fg : 文字颜...

Day1 - 前言

杂谈 今年是个很特别的一年,对威尔猪来说也是,从没写过任何形式的笔记或文章,也没什麽特别规划,居然就...

RxJS 错误处理 Operators (1) - catchError / finalize / retry / retryWhen

今天来介绍一些跟「错误处理」有关的 operators。在使用 RxJS 时,资料流是透过 pipe...

第 28 型 - 路由 (Router) - Resolve / 延迟载入 (Lazy Router)

上一篇利用路由机制传递参数来实作待办事项的编辑功能,除了透过网址路径来传递所需要的参数,还可以在路由...