iOS 微信openSDK使用Universal Links填坑

由于苹果爸爸推出的iOS 13系统版本对安全的升级,微信开发平台也跟风把SDK更新到1.8.6版本(搞事情?)其中最主要的就是将跳转方式改为Universal Links为的就是对发起分享的合法性校验,人家还说了以后要逐步收回旧版SDK的功能,说直白些就是如果你不适配就别用了,爱谁谁

关于Universal Links网上有很多技术贴,但是写的都非常零碎,本文着重介绍如何配置并集成最新版本的微信openSDK

废话到此结束,先上传送门:
GitHub源码Demo
苹果Universal Links官方文档
微信openSDK接入指引
关于授权登录,看另一个帖子
关于微信分享,看另一个帖子


配置要求

  1. SDK版本: 1.8.6或以上
  2. 微信版本: 7.0.7或以上
  3. 系统版本: iOS12或以上
  4. 服务器必须支持https,而且证书不得为自建证书(免费、不能通过验证的)

第一步,配置App

  1. 前往苹果开发者官网,找到并编辑你当前App所对应的Identifier;
  2. 检查有个“Associated Domains”选项,确认保持勾选;
    注意:如果当前未勾选,选择打开的同时会导致所有与当前Bundle ID相关的描述文件全部失效,需要重新配置;
  3. 记录下这里的Team ID和Bundle ID备用。
苹果开发者网站截图.png

第二步,配置服务器

这里需要后台童鞋支持一下,在host域名对应的服务器.well-known目录下面新建一个json文件,命名为apple-app-site-association
注意:不需要.json后缀,使用电脑浏览器打开https://{host}/.well-known/apple-app-site-association要可以下载该文件

配置apple-app-site-association内容为:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID":"MC9KP3T1Y2.com.wechat.DemoApp",
                "paths": [ "/wx_conn/*"]
            }
        ]
    }
}

这里个有几个注意事项:

  1. appID为固定值,即步骤一中的Team ID+Bundle ID,注意两者中间用.隔开;
  2. paths不是固定值,但是要注意微信在使用唤起第三方App时,会在Universal Links末尾拼接路径和参数,因此Universal Links配置必须加上星号通配符,不可以使用query。

第三步,配置Project

  1. 打开Xcode,选择project → Signing & Capabilities → + Capability 找到“Associated Domains”并添加;


    Xcode配置1.png
  2. 在Associated Domains选项中新增一项,格式要求applinks:开头,例如你的服务器域名为www.wehat.com,则设置为applinks:www.wehat.com
    Xcode配置2.png
  3. 使用真机验证是否可以通过Universal Links拉起微信App和你的App

验证拉起微信:使用Safari输入https://help.wechat.com/app/并打开,下拉查看是否有打开微信入口(如下图)

验证微信.png

验证拉起App:新建一个备忘录并输入刚才配置的Domains,也就是https://www.wechat.com/wx_conn/保存后,单击如果可以跳转到你的原生App,则说明配置成功。

注意:这里的/wx_conn/与apple-app-site-association中的paths相对应,请保证在域名对应服务器上配置完成后再尝试该步骤,否则备忘录会直接使用浏览器打开对应域名地址而不是跳转App)

第四步,配置微信开发平台

到微信开发平台的“管理中心”里,更新你的iOS移动应用:

  1. 查看详情,找到开发者信息一栏,点击右上角的“修改”按钮;


    微信开发平台截图.png
  2. 修改Universal Links路径,要求https开头,以“/”结尾,
    注意,这里的Universal Links路径需要使用到Domain并加上apple-app-site-association内paths通配符,例如:https://www.wechat.com/wwdc/https://www.wechat.com/wx_conn/app/(很多帖子包括微信的开发指南中说的都很模糊,所以耗费了很长时间)
  3. 保存,并记录这里的AppID和Universal Links备用

第五步,集成微信openSDK

将微信提供的openSDK(版本1.8.6或以上)下载并集成到你的工程内(详细过程查看iOS接入指南:https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html

  1. 配置URL Scheme,当分享结束后从微信回到原生App是通过URL Scheme跳转的,这里要使用从步骤四中从微信开发平台获取的AppID ,打开Xcode选择project → Info → URL Types → 新增一个,Identifier填weixin URL Schemes填AppID

    Xcode配置3.png

  2. 配置Info.plist,在LSApplicationQueriesSchemes下新增两个字段:weixinweixinULAPI

    Xcode配置4.png

    当然,如果你使用的API所在服务器不支持http,还需要加上App Transport Security Settings

  3. 代码部分,这里需要用到这里要使用从步骤四中从微信开发平台获取的App ID和Universal Links,在AppDelegate.m中实现如下代码:

#import "AppDelegate.h"
#import "WXApi.h"

#warning 注意修改WXAppId和WXUniversalLink为当前App对应在微信开放平台的配置
#define WXAppId @"wx123456789"//TODO:注意修改
#define WXUniversalLink @"https://www.wechat.com/wx_conn/app/"//TODO:注意修改

@interface AppDelegate ()<WXApiDelegate>

@end

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    
    [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString * _Nonnull log) {
        NSLog(@"%@", log);
    }];

    if([WXApi registerApp:WXAppId universalLink:WXUniversalLink]){
        NSLog(@"初始化成功");

        //自检函数
        [WXApi checkUniversalLinkReady:^(WXULCheckStep step, WXCheckULStepResult* result) {
            NSLog(@"%@, %u, %@, %@", @(step), result.success, result.errorInfo, result.suggestion);
        }];
    }
    
    return YES;
}

- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
    
    return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
    
    return [WXApi handleOpenURL:url delegate:self];
}

#pragma mark - Universal Link
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
    
    return [WXApi handleOpenUniversalLink:userActivity delegate:self];
}

#pragma mark - WXApiDelegate
- (void)onResp:(id)resp{

    if([resp isKindOfClass:[SendMessageToWXResp class]]){//微信回调
        
        SendMessageToWXResp *response = (SendMessageToWXResp *)resp;

        if(response.errCode == WXSuccess){
            //目前分享回调只会走成功
        }
    }else if([resp isKindOfClass:[SendAuthResp class]]){//判断是否为授权登录类

        SendAuthResp *req = (SendAuthResp *)resp;
        if([req.state isEqualToString:@"wx_oauth_authorization_state"]){//微信授权成功

        }
    }else if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]]){
        
        WXLaunchMiniProgramResp *req = (WXLaunchMiniProgramResp *)resp;
        NSString *string = req.msg; // 对应JsApi navigateBackApplication中的extraData字段数据
    }
}

@end

如果是在debug模式下真机调试,建议打开WXApi提供的log函数和自检函数,方便查看具体问题所在,但是后面打生产环境包前记得删除

  1. 真机测试,检查手机上安装的微信版本(需要7.0.7或以上),如果你在上面代码部分实现了自检函数[WXApi checkUniversalLinkReady:block];运行启动registerApp初始化通过后会主动拉起微信进行检查

WXULCheckStep值说明:
(1) step = WXULCheckStepParams: 参数检查
(2) step = WXULCheckStepSystemVersion: 当前系统版本检查
(3) step = WXULCheckStepWechatVersion: 微信客户端版本检查
(4) step = WXULCheckStepSDKInnerOperation: 微信SDK内部操作检查
(5) step = WXULCheckStepLaunchWechat: App拉起微信检查
(6) step = WXULCheckStepBackToCurrentApp: 由微信返回当前App检查
(7) step = WXULCheckStepFinal: 最终检查

自检函数会依次回调这7个step,当回调了WXULCheckStepFinal,说明检测通过,SDK接入成功。 任一step回调的result.success为NO, 流程终止,后续不再回调,可以根据result.errorInfo的查看当前步骤错误的原因,根据result.suggestion修复问题

  1. 如果上面自检函数步骤全检测通过那距离大功告成就已经不远了,发起分享:
if([WXApi isWXAppInstalled]){//判断当前设备是否安装微信客户端

    //创建多媒体消息结构体
    WXMediaMessage *message = [WXMediaMessage message];
    message.title = @"【爆款直降 盛夏特惠】【29.9免邮 限量买3免1】清新持久自然GUCCMI香水";//标题
    message.description = @"我在京东发现了一个不错的商品,赶快来看看吧。";//描述
    [message setThumbImage:[UIImage imageNamed:@"res2.png"]];//设置预览图

    //创建网页数据对象
    WXWebpageObject *webObj = [WXWebpageObject object];
    webObj.webpageUrl = @"[https://open.weixin.qq.com](https://open.weixin.qq.com)";//链接
    message.mediaObject = webObj;

    SendMessageToWXReq *sendReq = [[SendMessageToWXReq alloc] init];
    sendReq.bText = NO;//不使用文本信息
    sendReq.message = message;
    sendReq.scene = WXSceneSession;//分享到好友会话
    //发送分享信息
    [WXApi sendReq:sendReq completion:^(BOOL success) {
        NSLog(@"唤起微信:%@", success ? @"成功" : @"失败");
    }];
}else{

    //未安装微信应用或版本过低
}

当开发者的App发起分享时,微信SDK会校验Universal Links是否匹配,使用新版本SDK发起分享时,会出现如下图所示交互过程:从App拉起微信-出现“正在连接”页面-返回App-重新打开微信,这是新的安全验证流程,每个用户在首次使用时会出现上述跳转,理论上该过程只会出现一次,若同一用户发起分享多次都出现该跳转,则需要检查上面各步骤是否配置正确,下面附上我自己总结的流程图:

安全验证流程图.jpg

补充

我们都知道,开发者账号的Team ID是唯一的,应用的Bundle ID也是唯一的,那么问题来了:

  1. 需要同时适配多个App应用,该如何配置?
  2. 应用有两个Bundle ID(一个上架用,另一个测试用),该如何配置?

我个人对Universal Links的理解是通过多个paths来映射appID达到实现功能的目的,因此当分享完成后原路返回的时候是通过paths来匹配告诉系统应该唤起哪个App
所以解决办法就是对不同的appID配置不同的paths,否则当设备同时安装多个应用会出现分享完成后跳转异常

如果两个appID中配置的paths完全相同就会出现:
应用A→发起分享→跳转到微信内→选择好友/朋友圈发送→返回应用B
而理想情况应该是:
应用A→发起分享→跳转到微信内→选择好友/朋友圈发送→返回回应用A

照下面配置apple-app-site-association文件:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID":"MC9KP3T1Y2.com.wechat.app1",//Team ID+第一个App的Bundle ID
                "paths": [ "/wx_conn/1000001/*"]
            },
            {
                "appID":"MC9KP3T1Y2.com.wechat.app2",//Team ID+第二个App的Bundle ID
                "paths": [ "/wx_conn/1000002/*"]
            },
            {
                "appID":"MC9KP3T1Y2.com.wechat.app2_test",//Team ID+第三个App的Bundle ID
                "paths": [ "/wx_conn/1000002_test/*"]
            }
        ]
    }
}

完成上面步骤后,你需要前往微信开发平台中:
对app1配置Universal Link为:https://{host}/wx_conn/1000001/
对app2配置Universal Link为:https://{host}/wx_conn/1000002/
对app2_test配置Universal Link为:https://{host}/wx_conn/1000002_test/

另外,由于目前微信创建的单个移动应用只允许配置一个Universal Link,因此如果你需要使用另一个Bundle ID则需要开放平台另行创建一个移动应用

我们可以在AppDelegate.m中对应函数中打印webpageURL来论证分享完成后跳转回来时,是由paths决定返回哪个App的:

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {

    NSURL *url = userActivity.webpageURL;
    NSLog(@"%@", url.description);

    return YES;
}

打印内容为:https://{host}/wx_conn/1000001/wx123456789/
https://{host}/wx_conn/1000001/就是Universal Link
wx123456789是开发平台上的App ID

如果本文对你有所帮助记得点个赞哈:)

推荐阅读更多精彩内容