现有iOS工程接入
现有iOS工程接入
注意
在此之前请确保已经完成KMP侧 Kuikly的接入,如还未完成,请移步KMP跨端工程接入
完成Kuikly KMP侧的配置后, 我们还需要将Kuikly渲染器和适配器接入到宿主平台中,此文档适用于您想把Kuikly渲染器接入到您现有的iOS工程中。下面我们来看下,如何在现有iOS工程中接入Kuikly渲染器。
我们先新建一个名为KuiklyTest新工程并假设这个工程是你现有的iOS工程

添加Kuikly iOS 渲染器依赖
方式一:通过 CocoaPods 集成
- 添加
kuikly ios render
, 在你的工程的podfile添加以下代码
source 'https://cdn.cocoapods.org/'
platform :ios, '14.1'
target 'KuiklyTest' do
inhibit_all_warnings!
pod 'OpenKuiklyIOSRender', 'KUIKLY_RENDER_VERSION'
end
- 执行
pod install --repo-update
安装依赖
方式二:通过 SPM(Swift Package Manager)集成
Kuikly iOS 渲染器已支持通过 SPM 集成,推荐 Xcode 12 及以上版本使用。
1. 添加 Kuikly SPM 依赖
打开 Xcode,选择你的项目工程,点击左侧导航栏的 Project。
选择 Package Dependencies 标签页,点击右下角的 + 按钮。
在弹出的对话框中,输入 Kuikly iOS 渲染器的 Git 仓库地址:
https://github.com/kuikly/OpenKuiklyIOSRender.git
选择你需要的版本(建议与 KMP 工程保持一致),点击 Add Package。
在弹出的选择框中,勾选你的 Target,点击 Add Package 完成依赖添加。
提示
如需指定版本号,请选择与 KMP 工程一致的版本号。
2. 链接 Kuikly 业务代码 framework
Kuikly 业务代码在 iOS 平台会被编译为 .xcframework
,推荐以下集成方式:
推荐:通过 SPM 集成业务
.xcframework
建议将业务shared.xcframework
封装为一个本地或私有的 Swift Package,然后通过 SPM 集成到主工程。
步骤如下:新建一个 Swift Package(如
SharedFrameworkWrapper
)。在
Package.swift
中添加:.binaryTarget( name: "shared", path: "./shared.xcframework" )
将
shared.xcframework
拷贝到该包目录下。在主工程中通过 SPM 添加该 Package 依赖。
其他方式:
- CocoaPods 集成:在 Podfile 中添加业务模块的 pod 路径。
- 手动集成:将
shared.xcframework
拖入 Xcode 工程,并设置为Embed & Sign
。
3. 其他说明
- 适配器实现、Kuikly 容器等代码与 Pod 方式一致,参考下文实现即可。
- SPM 方式下,部分三方库(如 SDWebImage)如需使用,请自行通过 SPM 或 CocoaPods 集成。
实现Kuikly承载容器
在你现有的iOS工程中,新建KuiklyRenderViewController, 作为Kuikly
页面的容器。
具体实现代码,请参考源码工程iOSApp模块的KuiklyRenderViewController
类。
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface KuiklyRenderViewController : UIViewController
/*
* @brief 创建实例对应的初始化方法.
* @param pageName 页面名 (对应的值为kotlin侧页面注解 @Page("xxxx")中的xxx名)
* @param params 页面对应的参数(kotlin侧可通过pageData.params获取)
* @return 返回KuiklyRenderViewController实例
*/
- (instancetype)initWithPageName:(NSString *)pageName pageData:(NSDictionary *)pageData;
@end
NS_ASSUME_NONNULL_END
#import "KuiklyRenderViewController.h"
#import <OpenKuiklyIOSRender/KuiklyRenderViewControllerBaseDelegator.h>
#import <OpenKuiklyIOSRender/KuiklyRenderContextProtocol.h>
#define HRWeakSelf __weak typeof(self) weakSelf = self;
@interface KuiklyRenderViewController()<KuiklyRenderViewControllerBaseDelegatorDelegate>
@property (nonatomic, strong) KuiklyRenderViewControllerBaseDelegator *delegator;
@end
@implementation KuiklyRenderViewController {
NSDictionary *_pageData;
}
- (instancetype)initWithPageName:(NSString *)pageName pageData:(NSDictionary *)pageData {
if (self = [super init]) {
// 存储页面数据
pageData = [self p_mergeExtParamsWithOriditalParam:pageData];
_pageData = pageData;
// 实例化Kuikly委托者类
_delegator = [[KuiklyRenderViewControllerDelegator alloc] initWithPageName:pageName pageData:pageData];
_delegator.delegate = self;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// 通知Kuikly页面ViewDidLoad
[_delegator viewDidLoadWithView:self.view];
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// 通知Kuikly页面viewDidLayoutSubviews
[_delegator viewDidLayoutSubviews];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// 通知Kuikly页面viewWillAppear
[_delegator viewWillAppear];
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// 通知Kuikly页面viewDidAppear
[_delegator viewDidAppear];
[self.navigationController setNavigationBarHidden:YES animated:NO];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 通知Kuikly页面viewWillDisappear
[_delegator viewWillDisappear];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// 通知Kuikly页面viewDidDisappear
[_delegator viewDidDisappear];
}
#pragma mark - private
- (NSDictionary *)p_mergeExtParamsWithOriditalParam:(NSDictionary *)pageParam {
NSMutableDictionary *mParam = [(pageParam ?: @{}) mutableCopy];
return mParam;
}
#pragma mark - KuiklyRenderViewControllerDelegatorDelegate
// 创建等待加载视图
- (UIView *)createLoadingView {
UIView *loadingView = [[UIView alloc] init];
loadingView.backgroundColor = [UIColor whiteColor];
return loadingView;
}
// 创建加载错误视图
- (UIView *)createErrorView {
UIView *errorView = [[UIView alloc] init];
errorView.backgroundColor = [UIColor whiteColor];
return errorView;
}
// 设置业务代码编译成的framework名字
- (void)fetchContextCodeWithPageName:(NSString *)pageName resultCallback:(KuiklyContextCodeCallback)callback {
if (callback) {
// 返回对应framework名字
callback(@"shared", nil);
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
@end
实现Kuikly适配器(必须实现部分)
Kuikly
框架为了灵活和可拓展性,通过适配器的设计模式,将一些功能的具体实现委托给宿主App实现。
Kuikly
为iOS宿主工程提供了以下适配器
- 图片加载适配器: 用于给Kuikly的Image组件实现图片下载解码能力。宿主侧必须实现
- 页面路由适配器: 用于实现跳转到
Kuikly
容器。宿主侧必须实现 - 日志适配器: 用于给Kuikly框架和Kuikly业务实现日志打印。推荐宿主侧实现
- 异常适配器: 当Kuikly业务执行逻辑出错时,决定如何处理异常。推荐宿主侧实现
- 颜色值转换适配器: Kuikly框架对颜色值的处理,默认只处理十六进制的颜色值。宿主按需实现
- APNG图片加载适配器: 用于给Kuikly提供APNG图片加载的能力。宿主按需实现(使用APNG组件时必须实现)
实现图片加载适配器
具体实现代码,请参考源码工程iOSApp模块的KuiklyRenderComponentExpandHandler
类。
// .h
#import <Foundation/Foundation.h>
#import <OpenKuiklyIOSRender/KuiklyRenderBridge.h>
NS_ASSUME_NONNULL_BEGIN
/*
* @brief 需要接入方自定义实现图片加载
*/
@interface KuiklyRenderComponentExpandHandler : NSObject<KuiklyRenderComponentExpandProtocol>
NS_ASSUME_NONNULL_END
@end
// .m
#import "KuiklyRenderComponentExpandHandler.h"
#import <SDWebImage/UIImageView+WebCache.h>
@implementation KuiklyRenderComponentExpandHandler
+ (void)load {
// 注册自定义实现
[KuiklyRenderBridge registerComponentExpandHandler:[self new]];
}
/*
* 自定义实现设置图片
* @param url 设置的图片url,如果url为nil,则是取消图片设置,需要view.image = nil
* @return 是否处理该图片设置,返回值为YES,则交给该代理实现,否则sdk内部自己处理
*/
- (BOOL)hr_setImageWithUrl:(NSString *)url forImageView:(UIImageView *)imageView {
// 图片下载我们使用
[imageView sd_setImageWithURL:[NSURL URLWithString:url]];
return YES;
}
/*
...
@end
实现页面路由适配器
具体实现代码,请参考源码工程iOSApp模块的KRRouterHandler
类。
#import <Foundation/Foundation.h>
#import <OpenKuiklyIOSRender/KRRouterModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface KRRouterHandler : NSObject<KRRouterProtocol>
@end
NS_ASSUME_NONNULL_END
#import "KRRouterHandler.h"
#import "KuiklyRenderViewController.h"
@implementation KRRouterHandler
// 注册适配器
+ (void)load {
[KRRouterModule registerRouterHandler:[self new]];
}
// 打开页面
- (void)openPageWithName:(NSString *)pageName pageData:(NSDictionary *)pageData controller:(UIViewController *)controller {
KuiklyRenderViewController *renderViewController = [[KuiklyRenderViewController alloc] initWithPageName:pageName pageData:pageData];
[controller.navigationController pushViewController:renderViewController animated:YES];
}
// 关闭页面
- (void)closePage:(UIViewController *)controller {
[controller.navigationController popViewControllerAnimated:YES];
}
@end
实现日志适配器
该适配器Kuikly有默认实现,非必须实现, 业务可根据实际使用需求来决定是否实现。 具体实现代码,请参考源码工程core-render-ios模块的KuiklyLogHandler
类。
@interface KuiklyLogHandler : NSObject<KuiklyLogProtocol>
@end
@implementation KuiklyLogHandler
- (BOOL)asyncLogEnable {
return NO;
}
- (void)logInfo:(NSString *)message {
NSLog(@"%@", message);
}
- (void)logDebug:(NSString *)message {
#if DEBUG
NSLog(@"%@", message);
#endif
}
- (void)logError:(NSString *)message {
NSLog(@"%@", message);
}
@end
实现异常适配器
该适配器非必须实现, 业务可根据实际使用需求来决定是否实现。 如需实现,需重写KuiklyRenderViewControllerBaseDelegatorDelegate
协议的onUnhandledException:stack:mode:
方法,具体实现代码,请参考源码工程iOSApp模块的KuiklyRenderViewController
类。
@implementation KuiklyRenderViewController
- (void)onUnhandledException:(NSString *)exReason stack:(NSString *)callstackStr mode:(KuiklyContextMode)mode
{
// 处理异常或异常上报
}
@end
其他按需实现适配器示例参考实现适配器(按需实现部分)
链接Kuikly业务代码
Kuikly业务代码,在iOS平台上会被编译成.framework
产物,下一步,我们将编译好的Kuikly业务代码.framework
链接到你的工程。 .framework
的集成,你可以选择远程pod接入或者本地pod接入。这里为了方便,我只演示本地pod接入。
我们先前在Kuikly
KMP跨端工程接入中已经新建了Kuikly
业务工程,然后我们将这个业务工程的业务代码编译成的.framework
链接到我们的现有iOS工程
source 'https://cdn.cocoapods.org/'
platform :ios, '14.1'
target 'KuiklyTest' do
inhibit_all_warnings!
pod 'OpenKuiklyIOSRender', 'KUIKLY_RENDER_VERSION'
pod 'shared', :path => '/Users/XXX/workspace/TestKuikly/shared' # 本地存放Kuikly业务代码工程路径
end
重新执行pod install
安装依赖
编写TestPage验证
- 完成上述步骤后, 我们便完成了Kuikly的接入。下面我们在shared模块中编写TestPage,验证是否接入成功。我们在之前KMP跨端工程接入中新建的工程中的
shared
模块中新建TestPage
, 编写业务代码。新增的页面的名字为test
@Page("test")
class TestPage : Pager(){
override fun body(): ViewBuilder {
return {
attr {
allCenter()
}
Text {
attr {
fontSize(18f)
text("Hello Kuikly")
color(Color.GREEN)
}
}
}
}
}
- 接着我们初始化
KuiklyRenderViewController
,并将test
作为pageName
传入容器中, 指定跳转到我们刚新建的TestPage
中
- (void)pushKRView {
KuiklyRenderViewController *vc = [[KuiklyRenderViewController alloc] initWithPageName:@"test" pageData:nil];
[self.navigationController pushViewController:vc animated:YES];
}
- 当手机出现以下界面时, 说明接入已经成功接入Kuikly

实现适配器(按需实现部分)
实现颜色值转换适配器
通过该适配器自定义颜色转换逻辑,业务可根据实际使用需求来决定是否实现 具体实现代码,请参考源码工程iOSApp模块的KuiklyRenderComponentExpandHandler
类。
@implementation KuiklyRenderComponentExpandHandler
/*
* 自定义实现设置颜色值
* @param value 设置的颜色值
* @return 完成自定义处理的颜色对象
*/
- (UIColor *)hr_colorWithValue:(NSString *)value {
return nil;
}
实现APNG图片加载适配器
通过该适配器加载并显示APNG图片,当业务需要使用APNG组件时必须实现该适配器 具体实现代码,请参考源码工程iOSApp模块的KRAPNGViewHandler
类。
@interface KRAPNGViewHandler : SDAnimatedImageView<APNGImageViewProtocol>
@end
@implementation KRAPNGViewHandler
+ (void)load {
[KRAPNGView registerAPNGViewCreator:^id<APNGImageViewProtocol> _Nonnull(CGRect frame) {
KRAPNGViewHandler *apngView = [[KRAPNGViewHandler alloc] initWithFrame:frame];
return apngView;
}];
}
@end