# iOS Native Plugin Development Guidelines

# I. Development Environment Preparation

  1. according toNative Plug-In Tool Operating GuidelinesGenerate a multi-terminal plug-in project

  2. install Xcode, get ready iOS development environment

  3. iOS SDK >= 1.1.0

  4. Developer Tools Nightly >= 1.06.2311282

# II. Introduction to Plug-in Project

  • Here's an example of a multi-terminal plug-in project generated by the tool:

  • in Finder Open the multiterminal plug-in project directory in ios Folder, double-click on NativePlugin.xcodeproj Open the plug-in project

  • The effect when opened is as follows:

# Engineering Catalog Structure Dxplaination


    |-- Demo // Examples when developing plug-ins App Project, but there is no actual use in the development of multi-terminal plugins, developers do not need to change the contents of this folder
    |-- MyPlugin // Plugin main content, the developer's changes should be made in this directory
        |-- WeAppNativePlugin.framework // Multiterminal plugins and multiterminal cores SDK Communication-dependent static libraries
        |-- MyPlugin.h
        |-- MyPlugin.mm
    |-- resources // The resource directory you need to debug the plug-in
        |-- arm64 // For real machine debugging
        |-- x86 // For simulator debugging
        |-- script // Auxiliary scripts for development debugging
    

# engineering Xcode Configuration Dxplaination

The following configuration is used to create iOS Plug-in project has been set when the completion of the developer should not arbitrarily change

  • Demo BundleId: The transfer application that needs to be bound to the corresponding multiterminal application iOS Information matching

    Of a mobile app bound with multiple apps iOS Information is shown below:

    Plug-in bundleId Are automatically generated at the time of project creation, and developers can customize changes

  • Scheme:scheme The name is fixed as plugin And the plug-in build product name is formatted as ${pluginId}.framework

  • Build Phases:Run Script Phase handles the configuration of the build product using the plug-in Demo App, where the order and script content developers should not change.

# run

After completing the configuration according to the above guidelines, you can start running and debugging in Weixin DevTools.

  1. Switch in the WeChat Developer Tool toMultiterminal plug-in modeAfter, selectiOSto hitRun. The tool will open automatically Xcode Engineering.

  2. In the open Xcode Click here.runButton, you can see the sample project running effect

  3. in Xcode On the simulator, click firstLoad multiterminal plugins, Load successfully and then clickCalling the Multiterminal Plugin, can be obtained by vconsole See the return of the plug-in method.

    Note: If encountered M1 Computer Simulator button click no response, you can view[guide](https://dev.weixin.qq.com/docs/framework/faq/dev.html#_1% E3%80%81ios-m1-%E7%9A%84%E7%94%B5%E8%84%91%E5%87%BA%E7%8E%B0%E6%A8%A1%E6%8B%9F%E5%99%A8%E6%8C%89%E9%92%AE%E7%82%B9%E5%87%BB%E6%97%A0%E5%93%8D%E5%BA%94%E5%A6%82%E4%BD%95%E5%A4%84%E7%90%86)

  4. Subsequent developers modify Mini Program code, project.miniapagejson You just need to keep the tool onThe tool will work with Xcode Communication completes the corresponding resource replacement, the developer only needs to focus on the Xcode Recompile and run. in Xcode build log In, you can see Xcode Logs that communicate with the tool.

    The tool's build panel also has related log output:

    If the developer finds that the resource replacement does not take effect, Xcode build log Log that does not communicate with the tool or fails to communicate with the tool, please check yourself:

    • Tool version Nightly >= 1.06.2311282
    • toolSet up->Security settingsThe multiterminal plug-in service port is open in
    • The plug-in template has been updated to the latest (plug-in projects can be recreated)

# III. Plugin Development

# Introduction to Call Timing of Multiple-Application and Multiple-Plugins

In the beginning iOS Before the development of multi-terminal plug-ins, understanding the timing of multi-terminal applications and multi-terminal plug-in calls will help developers better.

sequenceDiagram
    participant app as Multiterminal application
    participant plugin as Multiterminal plugin

    app->>app:wx.miniapp.loadNativePlugin Load plugin
    app->>plugin: Load plugin framework
    plugin->>plugin: Instantiate the plug-in object on load
    app->>plugin: Load the plug-in, perform a series of initialization operations (including id Registration, plug-in method registration, plug-ins initPlugin Method call)
    app->>app: Hold Plugin Instance pluginInstance
    Note over app,plugin: Plugin Load Complete
    app->>plugin: pluginInstance.func() Make a call to the plug-in method

# Plugin development

Created in Environmental Preparation iOS Plug-in engineering template, completed for developers:

The following only Plug-in instantiation and Register plugin Id Is a necessary operation, the remaining operation developers can adjust according to their own needs.

# 1. Plug-in instantiation

__attribute__((constructor))
static void initPlugin() {
    [MyPlugin registerPluginAndInit:[[MyPlugin alloc] init]]
}

# 2. Register plugin Id

Using macro methods WEAPP_DEFINE_PLUGIN_ID(pluginId) Register plugin Id

WEAPP_DEFINE_PLUGIN_ID(YOUR_PLUGIN_ID)

# 3. Define plug-in instance methods initPlugin

// Plug-in initialization method, which is called automatically after registering the plug-in
- (void)initPlugin {
    NSLog(@"initPlugin")
}

# 4. Register synchronization method

  • Registration method

Using macro methods WEAPP_EXPORT_PLUGIN_METHOD_SYNC(methodName, methodSelector) Register the synchronization method.

parameter type Required Introductions
methodName yes in js Layer available for acquisition pluginInstance.methodName Make a method call
methodSelector SEL yes MyPlugin Plug-in instance methods defined Selector
  • Input and Output Parameters of Synchronization Method
parameter type Required Introductions
Entry NSDictionary * no in js Layer call pluginInstance.methodName When the incoming Object Type parameter
Ginseng Serializable type no in js Layer call pluginInstance.methodName The return of
  • Code Examples

OC Implementation of plug-in synchronization method

WEAPP_EXPORT_PLUGIN_METHOD_SYNC(mySyncFunc, @selector(mySyncFunc:))

- (id)mySyncFunc:(NSDictionary *)param {
    NSLog(@"mySyncFunc %@", param)
        
    return @"mySyncFunc"
}

JS Side of the method call


wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        console.log('load plugin success')
        const ret = plugin.mySyncFunc({ a: 'hello', b: [1,2] })
        console.log('mySyncFunc ret:',  ret)
    },
    fail: (e) => {
        console.log('load plugin fail', e)
    }
})

# 5. Register Asynchronous Methods

  • Registration method

Using macro methods WEAPP_EXPORT_PLUGIN_METHOD_ASYNC(methodName, methodSelector) Register an asynchronous method.

parameter type Required Introductions
methodName yes in js Layer available for acquisition pluginInstance.methodName Make a method call
methodSelector SEL yes MyPlugin Plug-in instance methods defined Selector
  • Input and output parameters of asynchronous methods
parameter type Required Introductions
Entry 1 NSDictionary * no in js Layer call pluginInstance.methodName When the incoming No. 1 individual Object Type parameter
Entry 2 WeAppNativePluginCallback no in js Layer call pluginInstance.methodName When the incoming No. 2 individual Function Type parameter, in the OC Side may be called back to the js Layer. The callback method supports a parameter of a serializable type
  • Code Examples

OC Plug-in asynchronous method implementation

WEAPP_EXPORT_PLUGIN_METHOD_ASYNC(myAsyncFuncwithCallback, @selector(myAsyncFunc:withCallback:))

- (void)myAsyncFunc: (NSDictionary *)param withCallback:(WeAppNativePluginCallback )callback {
    NSLog(@myAsyncFunc  %@", param)

    callback(@{ @"a": @"1", @"b": @[@1, @2], @"c": @3 })
}

JS Side of the method call


wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        console.log('load plugin success')
        plugin.myAsyncFuncwithCallback ({ a: 'hello', b: [1,2] }, (ret) => {
            console.log('myAsyncFuncwithCallback ret:',  ret)
        })
    },
    fail: (e) => {
        console.log('load plugin fail', e)
    }
})

# 6. registered AppDelegate method

multifarious iOS Plugin now supports listening AppDelegate method

  • application:openURL:options:
  • application:continueUserActivity:restorationHandler:
  • application:didFinishLaunchingWithOptions:
  • application:didRegisterForRemoteNotificationsWithDeviceToken:
  • application:didFailToRegisterForRemoteNotificationsWithError

Methods of Use

  1. The developer can inherit from the WeAppNativePlugin The method of registerAppDelegateMethod: To register a wiretap.
  2. A listening method is an instance method of a plug-in object where the method signature is consistent with the method to be monitored,
// in initPlugin Register to monitor
- (void)initPlugin {
    NSLog(@"initPlugin")
    [self registerAppDelegateMethod:@selector(application:openURL:options:)]
    [self registerAppDelegateMethod:@selector(application:continueUserActivity:restorationHandler:)]
}

// when App adopt URL Scheme When it is opened, it is called back here.
- (void)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
    NSLog(@"url scheme")
}

// when App adopt Universal Link When it is opened, it is called back here.
- (void)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void ()(NSArray<id<UIUserActivityRestoring>> *__nullable restorableObjects))restorationHandler {
    NSLog(@"universal link")
}

# 7. Plugin Event Listening

from iOS SDK >= 1.2.5 Start, support in the JS Side-listening from Native Of events.

  • in Native Side is inherited by calling the WeAppNativePlugin The method of sendMiniPluginEvent: May be directed to JS Side to send events.
parameter type Required Introductions
param NSDictionary * yes in js The input obtained by the listening method of the side
  • in JS Side that can use the plug-in instance onMiniPluginEvent Method Register Listening
parameter type Required Introductions
callback Function yes Native Lateral JS The callback triggered when an event is sent on the side, and multiple callbacks can be registered
  • in JS Side that can use the plug-in instance offMiniPluginEvent Cancel the wire.
parameter type Required Introductions
callback Function no Cancel the wire.Cancels all listening callbacks when no callback is specified to cancel
  • Code Examples

OC Plug-in asynchronous method implementation


- (void)sendMsg {
    [self sendMiniPluginEvent:@{ @"msg":  @"this is an event from plugin" }]
}

JS Side of the method call


const listener1 = (param) => {
  console.log('onMiniPluginEvent listener1', param)
}

const listener2 = (param) => {
  console.log('onMiniPluginEvent listener2', param)
}

wx.miniapp.loadNativePlugin({
    pluginId: 'YOUR_PLUGIN_ID',
    success: (plugin) => {
        plugin.onMiniPluginEvent(listener1)
        plugin.onMiniPluginEvent(listener2)
    }
})

# 8. Copy plug-in resources to main package

In general, the source files that plug-ins rely on are packaged and placed in dynamic libraries. If the developer needs to place the source file directly in the main package, it is available in the plugin project's MiniPlugin.bundle/PluginConfig.plist Fill in the configuration CopyResourcesToMainBundleAs shown in the following figure, when a multiterminal application is built, the MiniPlugin.bundle/resource.txt Copy to the build generated IPA Under the Level Directory.

Note: Source files to be copied to the main package should be placed in the MiniPlugin.bundle In.

# 9. Copy the Mini Program's resources into the plug-in

When using plug-ins, you can copy the resources in the Mini Program directory to the plug-in you use.

In the visual configuration of the project.miniapagejson file, under the plug-ins used, you can see "Add files to native plug-ins," configuring the relative path to the root path of the Mini Program project.

# 10. Start-on-load plugin

In general, plug-ins are dynamically attached only when loadNativePlugin is used. However, some scenarios require the plugin to be loaded as soon as the app launches.

In the development phase, In the plug-in's Mini Program project project.miniapagejson, configure loadWhenStart: true. You can simulate the app launch and load

  "mini-plugin": {
    "ios": {
      "loadWhenStart": true
    }
  }

During the use phase, in the visual configuration of the project.miniapagejson file, under the plug-in used, you can see "whether the app loads automatically when it starts."

Tools start support in version 1.06.2405222

# 11. App Extensions

Donut Plugin Development SupportApp ExtensionsSuch as message notification extensions, share components, desktop widgets, etc. xcode In the original development project,[Support development itself appex product](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG /ExtensionCreation.html)。

But in order to be able to use in donut, you need to appex Compiled scheme and target Name set to plugin id At the beginning. The compiled appex product is uploaded when building the plugin.

schemes

targets

At the time of final use, these appex All need to be signed. So the use of plug-ins Mini Program projects need to go to the Apple background configuration corresponding bundle id and Profiles, and added to project.miniapagejson.

  "mini-plugin": {
    "ios": [
      {
        "open": true,
        "pluginId": "wxaaaaaaaaaaaa",
        "pluginVersion": "1.0.0",
        "isFromLocal": false,
        "loadWhenStart": true,
        "appexProfiles":  {
          "NSE": {
            "enable": true,
            "bundleID": " xxxx.xxx.xxx .xxx, 
            "profilePath": "xxxx/xxxx/Development.mobileprovision " ,
            "distributeProfilePath":  "xxxx/xxxx/Certificate for distribution. mobileprovision "
          },
          "ShareExt": {
            "enable": true,
            "bundleID": " xxxx.xxx.xxx .xxx, 
            "profilePath": "xxxx/xxxx/Development.mobileprovision " ,
            "distributeProfilePath":  "xxxx/xxxx/Certificate for distribution. mobileprovision "
          }
        },
        "resourcePath": "pluginResources/wxaaaaaaa.json 
      }
    ],
    "android": []
  }

Tools start support in version 1.06.2405222

# to construct

Once the plug-in is debugged, the developer needs to build ${pluginId}.framework Dynamic library for use by multiple applications.

Provided in the toolto construct iOS Plug-in productOf buttons to help developers quickly build support for arm64 To the specified upload directory. iOS Plug-in products are specified to be placed in the directory build/ios Next, for subsequent uploading of dynamic libraries.

If the developer has a more personalized build process, they can build their own iOS Plug-in products, and put the plug-in products in the specified directory.

# Build failure positioning

Use in toolsto construct iOS Plug-in product, the essence is to call xcode Commands help developers build. In the event of tool build failure, the developer can ios Directory to locate the cause of the failure yourself using the following build script:

# Change to your own plugin id
PLUGIN_ID= " YOUR_PLUGIN_ID"

TEMP_BUILD_DIR=$(mktemp -d)

xcodebuild clean -project NativePlugin.xcodeproj -scheme plugin -derivedDataPath $TEMP_BUILD_DIR

xcodebuild -project NativePlugin.xcodeproj -scheme plugin -destination generic/platform=iOS -derivedDataPath $TEMP_BUILD_DIR -configuration Release ARCHS=arm64 ONLY_ACTIVE_ARCH=NO

xcodebuild -project NativePlugin.xcodeproj -scheme plugin -destination "generic/platform=iOS Simulator" -derivedDataPath $TEMP_BUILD_DIR -configuration Release ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO

FAT_PATH=$TEMP_BUILD_DIR/Build/Products
LINKMAP_PATH=$TEMP_BUILD_DIR/Build/Intermediates.noindex 
SIMULATOR_LIB_PATH=$FAT_PATH/Release-iphonesimulator
IPHONE_LIB_PATH=$FAT_PATH/Release-iphoneos

Li Po -create $IPHONE_LIB_PATH/$PLUGIN_ID.framework/$PLUGIN_ID $SIMULATOR_LIB_PATH/$PLUGIN_ID.framework/$PLUGIN_ID -output $FAT_PATH/$PLUGIN_ID

# IV. Use CocoaPods Managing Project Dependencies

Developers can refer touse CocoaPods Development iOS plug-in