uniapp ios原生应用集成uni小程序SDK教程

706 阅读8分钟

一、UniApp与原生App混合开发须知

首先务必确认uni-app和原生代码,谁是主谁是从的问题。两种情况如下:

1.1、uni-app为主

如果你的应用是uni-app开发的,需要扩展一些原生能力,那么首先去插件市场看看有没有现成的插件,如果没有,就自己开发。

原生插件开发的教程,可以参考我的另外一篇文章 《uniapp ios原生插件开发 (framework,cocopods)

1.2、ios原生为主

如果你的App是原生开发的,部分功能栏目想通过uni-app实现,有2种方式:

  • 在原生App里集成uni小程序sdk,参考:《uni小程序SDK》,相当于原生app拥有了小程序能力。
  • 如果不想集成原生sdk,那就把uni-app代码发布成H5方式,在原生App里通过webview打开。

接下来本文就重点介绍:在原生应用为主的前提下,如何进行uni小程序的集成 。

二、原生应用集成uniapp小程序

uni小程序SDK,是为原生App打造的可运行基于 uni-app 开发的小程序前端项目的框架,从而帮助原生App快速获取小程序的能力,效果如下:

在这里插入图片描述

2.1 uni小程序sdk下载

参见官网下载地址:《UniMP_iOS_SDK

下载成功后进行解压,解压后的目录结构如下:

在这里插入图片描述

其中,UniMPSDK目录为SDK lib库所在目录, 结构如下: 在这里插入图片描述

除SDK lib库所在的目录外,还包含了oc与swift的demo示例。

2.2 sdk集成

2.2.1 打开xcode新建一个App工程

在这里插入图片描述 在这里插入图片描述 输入工程名: HelloUniMPDemo, 完成工程创建, 完成后的工程如下:

在这里插入图片描述

2.2.2 集成SDK到工程中

将解压出来的uni小程序sdk UniMPSDK 目录拷贝到新建工程同目录下, 拷贝后的工程目录结构如下: 在这里插入图片描述

接着在打开的.xcodeproj工程中, 右键弹出菜单,选择Add Files to xxx

在这里插入图片描述

会弹出添加文件对话框, 选择与工程文件同目录下的Sdk库文件夹, 勾选上 Create groups

在这里插入图片描述

被引入后的目录结构如下:

在这里插入图片描述 接着去掉不需要的文件引用,只保留核心库。 如下图,红框内的文件,删除文件引用 在这里插入图片描述 在这里插入图片描述

点击Remove References 按钮,删除文件引用。 处理完成后的目录结构如下:

在这里插入图片描述

至此,我们已经把uni小程序的SDK集成到工程中了, 编译一下工程,看是否能编译通过。(正常情况下,是可以成功编译的)

2.3 sdk初始化

1) 首页,在AppDelegate.m 中导入头文件 DCUniMP.h, WeexSDK.h

#import "AppDelegate.h"

#import "DCUniMP.h"
#import "WeexSDK.h"

@interface AppDelegate ()

@end

2) 然后在代理方法 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中进行初始化

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    // 配置参数
    NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
    // 设置 debug YES 会在控制台输出 js log,默认不输出 log,注:需要引入 liblibLog.a 库
    [options setObject:[NSNumber numberWithBool:YES] forKey:@"debug"];
    // 初始化引擎
    [DCUniMPSDKEngine initSDKEnvironmentWithLaunchOptions:options];
  
    return YES;
}

3) 监听App 生命周期方法


#pragma mark - App 生命周期方法
- (void)applicationDidBecomeActive:(UIApplication *)application {
    [DCUniMPSDKEngine applicationDidBecomeActive:application];
}
 
- (void)applicationWillResignActive:(UIApplication *)application {
    [DCUniMPSDKEngine applicationWillResignActive:application];
}
 
- (void)applicationDidEnterBackground:(UIApplication *)application {
    [DCUniMPSDKEngine applicationDidEnterBackground:application];
}
 
- (void)applicationWillEnterForeground:(UIApplication *)application {
    [DCUniMPSDKEngine applicationWillEnterForeground:application];
}
 
- (void)applicationWillTerminate:(UIApplication *)application {
    [DCUniMPSDKEngine destory];
}

可选,非必须: 如果用到以下AppDelegate的回调,则实现对应的方法。没用到则不需要实现。

#pragma mark - 如果需要使用 URL Scheme 或 通用链接相关功能,请实现以下方法
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
    // 通过 url scheme 唤起 App
    [DCUniMPSDKEngine application:app openURL:url options:options];
    return YES;
}
 
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
    // 通过通用链接唤起 App
    [DCUniMPSDKEngine application:application continueUserActivity:userActivity];
    return YES;
}
 
#pragma mark - 如需使用远程推送相关功能,请实现以下方法
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    // 远程通知注册成功,收到 deviceToken 调用sdk方法,传入 deviceToken
    [DCUniMPSDKEngine application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
 
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    // 远程通知注册失败
    [DCUniMPSDKEngine application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
 
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    // 收到远程推送消息
    [DCUniMPSDKEngine application:application didReceiveRemoteNotification:userInfo];
    completionHandler(UIBackgroundFetchResultNewData);
}
 
#pragma mark - 如需使用本地推送通知功能,请实现以下方法
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    // 收到本地推送消息
    [DCUniMPSDKEngine application:application didReceiveLocalNotification:notification];
}

4) 最后编译程序。 你会发现编译报错, 出现很多错误信息。 根据错误信息在 项目的 Build Pase-> link binary with Libraries 添加如下系统库:

libc++.tdb
JavaScriptCore.framework
MediaPlayer.framework
AddressBook.framework
CoreLocation.framework
CoreTelephony.framework
QuickLook.framework
WebKit.framework
AssetsLibrary.framework

此外,还需要在项目的 Build Settings -> Other link flags 中添加 -ObjC, 且将项目的 BitCode的值 设置为 NO 在这里插入图片描述

在这里插入图片描述

通过上面的一顿操作后,再尝试重新编译。 如果都配置正常,则项目工程可编译通过。

2.4 sdk的使用

1) 在UniMPSDK下创建一个group: Apps,sdk解压缩包HelloUniMPDemo/UniMP/Apps拷贝一个wgt到Apps下面。

在这里插入图片描述

2) 添加加载测试小程序逻辑。

/// 启动小程序
- (void)openUniMP {
    // 获取配置信息
    DCUniMPConfiguration *configuration = [self getUniMPConfiguration];
    
    // 配置启动小程序时传递的参数(参数可以在小程序中通过 plus.runtime.arguments 获取此参数)
    configuration.arguments = @{ @"arguments":@"Hello uni microprogram" };
    
    __weak __typeof(self)weakSelf = self;
    // 打开小程序
    [DCUniMPSDKEngine openUniMP:k_AppId configuration:configuration completed:^(DCUniMPInstance * _Nullable uniMPInstance, NSError * _Nullable error) {
        if (uniMPInstance) {
            weakSelf.uniMPInstance = uniMPInstance;
        } else {
            NSLog(@"打开小程序出错:%@",error);
        }
    }];
}

3) 实现DCUniMPSDKEngineDelegate的方法。

完整的代码如下:

#import "UniappViewController.h"
#import "DCUniMP.h"
 
#define k_AppId @"__UNI__11E9B73" // 小程序名称
 
@interface UniappViewController ()<DCUniMPSDKEngineDelegate>
 
@property (nonatomic, weak) DCUniMPInstance *uniMPInstance; // 声明DCUniMPInstance
 
@end
 
@implementation UniappViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(labelTouchUpInside:)];
    
    UILabel *lable = [[UILabel alloc] initWithFrame:CGRectMake(100,100, 100,100)];
    lable.text = @"打开小程序";
    lable.textColor = [UIColor redColor];
    lable.userInteractionEnabled = YES;
    [lable addGestureRecognizer:tap];
    
    [self.view addSubview:lable];
    
    [self checkUniMPResource];
}
 
-(void) labelTouchUpInside:(UITapGestureRecognizer *)recognizer{
   UILabel *label=(UILabel*)recognizer.view;
   NSLog(@"%@被点击了",label.text);
    
    [self openUniMP];
}
/// 小程序配置信息
- (DCUniMPConfiguration *)getUniMPConfiguration {
    /// 初始化小程序的配置信息
    DCUniMPConfiguration *configuration = [[DCUniMPConfiguration alloc] init];
    
    // 开启后台运行
    configuration.enableBackground = YES;
    // 设置 push 打开方式
    configuration.openMode = DCUniMPOpenModePush;
    // 启用侧滑手势关闭小程序
    configuration.enableGestureClose = YES;
    
    return configuration;
}
 
/// 检查运行目录是否存在应用资源,不存在将应用资源部署到运行目录
- (void)checkUniMPResource {
    if (![DCUniMPSDKEngine isExistsApp:k_AppId]) {
        // 读取导入到工程中的wgt应用资源
        NSString *appResourcePath = [[NSBundle mainBundle] pathForResource:k_AppId ofType:@"wgt"];
        if (!appResourcePath) {
            NSLog(@"资源路径不正确,请检查");
            return;
        }
        // 将应用资源部署到运行路径中
        if ([DCUniMPSDKEngine releaseAppResourceToRunPathWithAppid:k_AppId resourceFilePath:appResourcePath]) {
            NSLog(@"小程序 %@ 应用资源文件部署成功",k_AppId);
        }
        
        // 设置 delegate
        [DCUniMPSDKEngine setDelegate:self];
    }
}
 
/// 启动小程序
- (void)openUniMP {
    // 获取配置信息
    DCUniMPConfiguration *configuration = [self getUniMPConfiguration];
    
    // 配置启动小程序时传递的参数(参数可以在小程序中通过 plus.runtime.arguments 获取此参数)
    configuration.arguments = @{ @"arguments":@"Hello uni microprogram" };
    
    __weak __typeof(self)weakSelf = self;
    // 打开小程序
    [DCUniMPSDKEngine openUniMP:k_AppId configuration:configuration completed:^(DCUniMPInstance * _Nullable uniMPInstance, NSError * _Nullable error) {
        if (uniMPInstance) {
            weakSelf.uniMPInstance = uniMPInstance;
        } else {
            NSLog(@"打开小程序出错:%@",error);
        }
    }];
}
 
#pragma mark - DCUniMPSDKEngineDelegate
/// DCUniMPMenuActionSheetItem 点击触发回调方法
- (void)defaultMenuItemClicked:(NSString *)identifier {
    NSLog(@"标识为 %@ 的 item 被点击了", identifier);
    
    // 将小程序隐藏到后台
    if ([identifier isEqualToString:@"enterBackground"]) {
        __weak __typeof(self)weakSelf = self;
        [self.uniMPInstance hideWithCompletion:^(BOOL success, NSError * _Nullable error) {
            if (success) {
                NSLog(@"小程序 %@ 进入后台",weakSelf.uniMPInstance.appid);
            } else {
                NSLog(@"hide 小程序出错:%@",error);
            }
        }];
    }
    // 关闭小程序
    else if ([identifier isEqualToString:@"closeUniMP"]) {
        [self.uniMPInstance closeWithCompletion:^(BOOL success, NSError * _Nullable error) {
            if (success) {
                NSLog(@"小程序 closed");
            } else {
                NSLog(@"close 小程序出错:%@",error);
            }
        }];
    }
    // 向小程序发送消息
    else if ([identifier isEqualToString:@"SendUniMPEvent"]) {
        [DCUniMPSDKEngine sendUniMPEvent:@"NativeEvent" data:@{@"msg":@"native message"}];
    }
}
 
/// 返回打开小程序时的自定义闪屏视图
- (UIView *)splashViewForApp:(NSString *)appid {
    UIView *splashView = [[[NSBundle mainBundle] loadNibNamed:@"SplashView" owner:self options:nil] lastObject];
    return splashView;
}
 
/// 小程序关闭回调方法
- (void)uniMPOnClose:(NSString *)appid {
    NSLog(@"小程序 %@ 被关闭了",appid);
    
    self.uniMPInstance = nil;
}
 
/// 监听小程序发送的事件回调方法
/// @param event 事件
/// @param data 参数
/// @param callback 回调方法,回传数据给小程序
- (void)onUniMPEventReceive:(NSString *)event data:(id)data callback:(DCUniMPKeepAliveCallback)callback {
    
    NSLog(@"Receive UniMP event: %@ data: %@",event,data);
    // 回传数据给小程序
    // DCUniMPKeepAliveCallback 用法请查看定义说明
    if (callback) {
        callback(@"native callback message",NO);
    }
}
 
@end

在Xcode中运行代码,看到测试小程序被正常调起了,说明uniapp开发的小程序集成OK了。

三、uniapp小程序SDK使用说明

3.1 应用场景

  • 宿主App构建自己的应用生态,可以面向广泛开发者,也可以定向部分开发者
  • 原生App利用小程序模式对模块解耦,让不同模块的开发团队各自独立发版,灵活更新
  • 原生App中部分功能使用uni-app实现,降低开发成本、提升发布效率

3.2 集成流程

  • 宿主开发者下载uni小程序sdk,根据文档集成到宿主App中
  • 小程序开发者使用 HBuilderX 创建 uni-app 项目并开发功能,开发阶段可以直接在 HBuilderX 内置基座进行测试,开发完毕后在发行菜单导出wgt包
  • 宿主开发者将 wgt 集成到宿主App中,可离线集成直接打包进去,也可以在线下载wgt
  • 通过 uni小程序sdk 的 api 将 wgt 资源部署到运行路径,然后即可跳转到该资源对应的小程序应用中
  • uni小程序支持 wgt升级,可以在小程序中直接调用 api 在线更新 wgt 资源,即热更新 参考文档

3.3 名词解释

  • 宿主:想要构建小程序的原生App,集成uni小程序SDK的宿主
  • 小程序:运行在宿主中的前端代码编写的小程序项目,使用 uni-app 框架开发
  • wgt:小程序应用资源包,即使用 uni-app 框架开发的项目,导出的小程序应用资源包
  • HBuilderX:开发 uni-app 的 IDE 工具

3.4 注意事项

  • uni小程序sdk 仅支持在原生App中集成使用,不支持 HBuilderX 打包生成的App中集成。如需在uni-app项目中使用,请加QQ群984388064申请
  • uni小程序sdk 支持同时运行多个小程序实例,但android平台最多打开三个
  • uni 小程序 sdk 无法使用插件市场中付费的原生插件