使用动态framework封装微信SDK实践2019-11-14
需求
对外提供一个SDK
,功能是拉起微信小程序。
分析
-
实现原理:在将要开发的
SDK
中,封装微信的SDK
,通过微信SDK
,从而达到拉起微信小程序的目的 -
以前项目中,接入过微信
SDK
,是1.8.4
版本的,使用了其中的分享图片到朋友圈的功能,比较顺利,是通过CocoaPod
实现的,比较省心。 -
随着
iOS13
的发布,现在的微信SDK
升级到了1.8.6.1
,增加了通用链接功能。并且通用链接是必选项,初始化函数增加了参数,否则就失败。
[WXApi registerApp:APP_ID universalLink:UNIVERSAL_LINK];
尝试过绕开这个通用链接,比如用百度的网址占位,这个函数一直返回NO
。 -
说实话,通用链接真的很麻烦,看看微信给的介绍文章,说了一大堆,还没把问题说清楚。并且通用链接只是一个用
Safari
浏览器来打开app
,比较鸡肋。说是增加安全性,这里也用不到。这里只是简单拉起微信小程序就可以了,不需要回传信息。
关于openSDK1.8.6的更新说明 -
提供了一个鸡肋功能,导致以前的版本不能用,也不考虑兼容性,微信的
SDK
做得真的很差。折腾了好几天,最终还是决定用相对简单易用的1.8.4
,避开繁琐而没用的通用链接。 -
网上说
iOS
的SDK
一般都是静态库,比如微信提供的SDK
就是.a
形式的静态库。我也试着封装了一下,发现使用起来很麻烦。比如我们提供的静态库叫my.a
,用户使用的时候,要把my.a
和对应的头文件my.h
导入工程。由于my.a
用到了微信的libWeChatSDK.a
,所以用户也要把libWeChatSDK.a
和配套的头文件导入工程。另外,libWeChatSDK.a
还用到了很多系统库,微信文档不准确,自己查了好久的百度才link
成功。这部分工作,用户也要重复一遍,想想都麻烦。
既然这样,封装一层
my.a
又有什么意义,直接使用libWeChatSDK.a
不就行了?
- 封装成动态
framework
的话,至少使用libWeChatSDK.a
时遇到的麻烦事只要做一遍就好了,这样才有封装的意义。
结论:使用动态framework
封装WeChatSDK1.8.4
实现拉起微信小程序的功能
开发动态framework
Setp1: 创建framework
工程
以framework
的名字为工程名字,Xcode
的菜单路径为File->New->Project...
,在弹出菜单中,类型选择framework
工程创建完成后,会自动生成一个和framework
同名的h
文件,这个文件是必须的,不能删除。
Setp2: 修改输出文件
- 默认情况下,只有与
framework
同名的h
文件才能输出。如果项目不大,有这么一个输出文件就够了。但是,只有一个h
文件不是很方便,可以用一个继承自NSObject
的类代替,名字和framework
同名。在头文件中定义输出函数。
- 外部可以使用的文件在
Build Phases -> Headers
标签下,从Project
部分移动到Public
部分
Step3:导入微信SDK
-
这里选用的是
WeChatSDK1.8.4
,以前从微信开发者平台下的,这个版本不需要通用链接,比较方便。现在网上能下的版本是WeChatSDK1.8.6.1
,要求通用链接,用起来很麻烦,并且向前不兼容,效果很差,抛弃不用。 -
微信
SDK
是静态的.a
文件,将整个文件夹通过Xcode
菜单File->Add files to ...
就可以了。 -
新建一个以
NSObject
为基类的自定义类,这里是KJTWeiXin
,通过调用微信SDK
的相关函数,完成拉起微信小程序的功能。 -
在输出文件,这里是
KJTUrlSdk.m
,调用刚才的实现,完成的功能实现的导出工作。
Step4:创建调试Target
-
framework
是不能直接使用的,需要一个调用者APP
。这里采用Target
的方式(菜单路径File->New->Target...
),名字,在framework
名字后面加-Sample
后缀,(这里是),类型就选最简单的单页面工程。
- 建立项目依赖。使用者,(这里是KJTUrlSdk-Sample),依赖framework,(这里是KJTUrlSdk)。
合并framework
生成的framework
要么是真机的,要么是模拟器的,很不方便。用命令行也比较烦,用脚本的方式比较好。
-
Build Phase中的New Run Script Phase
可以添加自动运行的脚本
- 脚本内容如下:
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
open "${DEVICE_DIR}"
open "${SRCROOT}/Products"
fi
- 每次运行都会生成一遍,模拟器和真机至少运行一次之后,在工程根目录下的
Product
文件夹下就是合并过的framework
,真机和模拟都能用。
Step5: Demo工程
-
创建一个独立的工程,使用自己开发的
framework
。这个工程要和前面的开发工程完全独立。名字可以加-Demo
后缀,(这里是KJRUrlSdk-Demo
) -
通过菜单Flie->Add files to ... 将合并过的framework添加进来。
注意:如果要提交苹果市场,只能用真机版本的framework,否则会出错。
- 在General标签下,将framework的载入格式改为Embed&Sign,否则会奔溃。
如果不改,崩溃信息一般如下:
dyld: Library not loaded: @rpath/KJTUrlSdk.framework/KJTUrlSdk
Referenced from: /var/mobile/Containers/Bundle/Application/CFEEFAD2-1BF3-4D29-967B-42CAAB7C4811/KJTUrlSdk-Demo.app/KJTUrlSdk-Demo
Reason: image not found
最后的样子:经过以上各步骤,最后的文件结构样子大致如下
企业微信截图_46c1c67c-ca66-408c-8b50-0b37fb30f330.png微信SDK依赖系统库
- 微信
WeChatSDK1.8.4
依赖如下系统库
-
如果不添加这些系统库,会出现链接错误,并且这些问题,微信的开发文档并没有完全说清楚。只能百度,一个个试过去,比较麻烦。
导入微信支付报错解决 -
使用动态
framework
包含一个静态.a的好处是上面那些依赖可以在framework
内部处理掉,最终使用者,这里是KJTUrlSdk-Demo
,就不需要再考虑这些麻烦事了。
注册函数导致崩溃。
- 由于是MTA库中的剪贴板和其他程序竞争,导致崩溃。只要不打开MTA就好了。
WXApi registerApp:@"xxx" enableMTA:NO];
[__NSArrayM enqueue:]: unrecognized selector sent to instance
- WeChatSDK1.8.5之后的版本解决了这个问题。
检查微信是否安装函数不可用
这是因为微信SDK使用[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"weixin://"]]
方式来检查是否安装微信。iOS9
之后,要在Plist中加白名单才可以用。不然这个函数始终返回NO
应用BundleID信息校验不通过
- 如果使用者
APP
的bundle ID
,(这里是KJTUrlSdk-Demo
和KJTUrlSdk-Sample
),和微信后台配置的bundle ID
不一致,就会弹这个信息框,小程序跳转也有异常。但是,微信API函数执行还是成功的。
framework的版本号
-
framework
工程创建的时候,会自动给出两个关于版本的全局变量,一个是double
型的,一个是char
数组
- 这两个变量对应的是
Target->General
下的Build
,并不是通常理解的Version
-
double
型的数字只能显示两位,比如设置1.2.3.4
,显示1.200000
;字符型的能显示全,不过会加上很多前缀。比如Build
配置为1.2.3.4
,通过NSLog
输出:
NSLog(@"数字版本号:%f", KJTUrlSdkVersionNumber); // 数字版本号:1.200000
NSLog(@"字符数组版本号:%s", KJTUrlSdkVersionString); // 字符数组版本号:@(#)PROGRAM:KJTUrlSdk PROJECT:KJTUrlSdk-1.2.3.4
- 参考文章:iOS Framework-版本号指定
如何生成Release模式的framework?
-
一般的
scheme
,都是Run
的时候Debug,Archive
的时候Release
。这个设定对于可执行文件是可行的,但是对于framework
就不合适了。因为framework
无法单独执行,没法Archive
-
可以将
framework
的Run
改为Release
。就算这样,用断点调试,也是可以停住的,不影响调试效果。可以这样说,Debug
模式对于framework
来说是没有意义的,统一使用Release
就好了。