资讯详情

资讯详情

Unity iOS接入微信登录与分享:从配置到避坑全流程

1. 接入前的整体设计与思路拆解去年我在做一款上线iOS的Unity游戏时需要接入微信登录和分享功能。本来以为这活儿跟Android端差不多调个SDK、写几个方法就行结果真正动手才发现iOS端的流程比想象中繁琐得多坑也更多。写这篇文章就是为了把这个过程完整记录下来尤其是几个容易卡住的细节。先说清楚你将要面对的是什么样的技术链路。微信官方提供的Unity SDK其实已经封了一层但它在iOS上的实现依赖的是原生Objective-C代码通过Unity的RuntimeExport机制和UIApplicationDelegate消息转发来打通Unity层和iOS原生层。所以你的Unity工程里最终会同时存在C#脚本和Objective-C脚本两者之间通过UnitySendMessage互相传消息。整个接入过程可以拆成三大块微信开放平台侧的配置拿AppID、配置Bundle ID、配置Universal Links这块属于一个都不能错的前置条件错了后面全白搭。iOS原生层把微信SDK集成到Xcode工程里实现WXApiDelegate把微信回调转发给Unity。Unity C#层调起登录、调起分享、接收回调结果并处理。我选择的方案是不用微信官方Unity SDK直接在Unity工程里自己写Objective-C的桥接代码。原因有两个一是官方Unity SDK的更新节奏经常跟不上微信SDK的更新二是自己写桥接代码能完全掌控回调链路排查问题的时候思路清晰。当然如果你只是想快速实现用官方Unity SDK也不是不行只是后面如果遇到问题你可能会跟我一样要拆开SDK源码去翻逻辑。提示本文的步骤基于Unity 2021.3 LTS和微信SDK 2.0.22024年初版本不同版本的操作细节可能有差异但整体流程是通用的。2. 微信开放平台配置漏掉任何一项都会让你怀疑人生很多人习惯先把代码写上写完再去开放平台注册。我建议反过来先花十分钟把开放平台的配置搞定因为AppID和Universal Links是写死在代码里的后期再改就是一遍全量打包重来。2.1 应用注册与AppID登录微信开放平台在管理中心创建移动应用。这里要注意一个细节iOS的Bundle ID必须和你Xcode工程里的Bundle Identifier完全一致。我见过有人在这里填错一个字母结果所有API调用都返回签名错误。创建完成后你会拿到一个AppID形如wx1234567890abcdef和AppSecret。AppSecret在iOS端基本用不到但建议保存好后面调试分享参数详情时可能会用到。AppSecret不要写进客户端代码里微信SDK的iOS端设计是签名在服务端做客户端只需要AppID就能调起微信。如果你在客户端里塞了AppSecret上架审核时会因为客户端包含敏感密钥被打回。2.2 Universal Links配置Universal Links是微信登录和分享在iOS上的通行凭证。它的本质是微信服务器验证你的App和域名之间的绑定关系然后在调起微信后微信通过Universal Links回调你的App。这个配置分成三步在你的服务器上放一个apple-app-site-association文件内容格式像这样{ applinks: { apps: [], details: [ { appID: TEAMID.com.yourcompany.yourgame, paths: [/ios/*] } ] } }注意appID的格式是TeamID . Bundle ID。TeamID在苹果开发者后台的Membership Details里能找到不是App ID Prefix这俩我当初绕了半小时才搞明白。文件必须能通过HTTPS访问且不能有重定向。我建议你配置完后直接Safari打开https://yourdomain.com/apple-app-site-association确认能访问到内容。如果你的CDN有二次重定向iOS是认不出来的。在Xcode工程里配置Associated Domains加applinks:yourdomain.com。这里有个很容易踩的坑模拟器上测试Universal Links经常失败。模拟器的Universal Links验证机制跟真机不一样有时候你在模拟器上试了不行真机上其实是好的。所以从一开始就用真机调试别浪费时间在模拟器上。2.3 URL Scheme配置微信SDK还需要配置URL Scheme。在Xcode工程里找到Info.plist的URL Types添加一个URL Scheme值就是你的AppID。这个URL Scheme的作用是微信通过OpenURL的方式把结果回调给你的App。Info.plist里要有这几项LSApplicationQueriesSchemes添加weixin和weixinULAPI否则iOS 9以后无法判断微信是否安装。NSAppTransportSecurity如果你的Universal Links服务端走的是HTTP不建议需要配置NSAllowsArbitraryLoads。但我强烈建议全部走HTTPS省得后面被审核盯着问。CFBundleURLTypes添加你的AppID作为URL Scheme。这三项配置缺一个微信SDK的表现会非常奇怪——不是报已安装判断错误就是回调完全收不到。3. iOS原生层实现Objective-C桥接的完整写法配置做完开始写代码。这一节的代码是需要在Unity工程里新建一个Assets/Plugins/iOS目录放一个Objective-C文件内容长这样下面会逐步拆解3.1 导入微信SDK微信SDK的下载包里有libWechatSDK.a这个静态库还有一堆头文件。你需要把.a文件和头文件整体拖进Assets/Plugins/iOS目录。给.a文件添加-ObjC标志。这个操作在Xcode里做Build Settings-Other Linker Flags- 添加-ObjC。不加这个标志你会在调用微信API时报一堆unrecognized selector错误。顺带一提微信SDK有两个版本WechatAuthSDK用于语音识别等能力和WeChatSDK核心登录/分享。我们只需要后者别一股脑全拖进去。3.2 AppDelegate的消息转发微信SDK要求你继承WXApiDelegate并在AppDelegate里转发两个方法。但由于Unity工程里的AppDelegate是从UnityAppController继承来的你不能直接修改Unity生成的AppDelegate得通过RuntimeExport来注入方法。在Objective-C文件里写一个分类Category#import UIKit/UIKit.h #import WXApi.h #import UnityAppController.h interface UnityAppController (WeChat) WXApiDelegate end implementation UnityAppController (WeChat) - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { return [WXApi handleOpenURL:url delegate:self]; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionaryUIApplicationOpenURLOptionsKey, id *)options { return [WXApi handleOpenURL:url delegate:self]; } - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArrayidUIUserActivityRestoring * _Nullable))restorationHandler { return [WXApi handleOpenUniversalLink:userActivity delegate:self]; } - (void)onReq:(BaseReq *)req { // 微信主动发起的请求一般这里不需要处理 } - (void)onResp:(BaseResp *)resp { if ([resp isKindOfClass:[SendAuthResp class]]) { SendAuthResp *authResp (SendAuthResp *)resp; NSString *code authResp.code ?: ; NSString *result [NSString stringWithFormat:login_%_%, code, (resp.errCode)]; UnitySendMessage(WeChatManager, OnLoginCallback, result.UTF8String); } else if ([resp isKindOfClass:[SendMessageToWXResp class]]) { SendMessageToWXResp *shareResp (SendMessageToWXResp *)resp; NSString *result [NSString stringWithFormat:share_%, (resp.errCode)]; UnitySendMessage(WeChatManager, OnShareCallback, result.UTF8String); } } end这里有几个关键点要说明第一RuntimeExport其实不需要额外写代码。Unity的UnityAppController在启动时会扫描所有分类Category把实现过的方法自动跟UIApplicationDelegate绑定。你只需要确保分类里的方法签名跟UIApplicationDelegate一致就行。我最初不知道这个机制以为还要手动注册实际无需多此一举。第二onResp里的UnitySendMessage是Unity给原生层的对话窗口。这个方法在Unity启动后随时可用它会找到名为WeChatManager的GameObject调用上面的OnLoginCallback方法。这个GameObject必须提前存在于场景里而且名字要完全匹配否则消息会静默丢失——这是我最常遇到的回调消失问题。第三errCode是微信返回的错误码0表示成功-2表示用户取消。在onResp里不要做复杂的业务逻辑把它原样转发给Unity层处理就行。原生层越傻越不容易出问题。3.3 登录和分享的调用方法调起微信登录需要在Unity层发指令给原生层同样用RuntimeExport机制。在Objective-C文件里加一个方法extern C { void _WeChatLogin() { SendAuthReq *req [[SendAuthReq alloc] init]; req.scope snsapi_userinfo; req.state your_custom_state; [WXApi sendReq:req completion:nil]; } void _WeChatShareText(const char *text, int scene) { SendMessageToWXReq *req [[SendMessageToWXReq alloc] init]; req.bText YES; req.text [NSString stringWithUTF8String:text]; req.scene (int)scene; // WXSceneSession 0, WXSceneTimeline 1 [WXApi sendReq:req completion:nil]; } void _WeChatShareLink(const char *title, const char *desc, const char *url, const char *thumbImagePath, int scene) { WXMediaMessage *message [WXMediaMessage message]; message.title [NSString stringWithUTF8String:title]; message.description [NSString stringWithUTF8String:desc]; [message setThumbImage:[UIImage imageWithContentsOfFile:[NSString stringWithUTF8String:thumbImagePath]]]; WXWebpageObject *webObj [WXWebpageObject object]; webObj.webpageUrl [NSString stringWithUTF8String:url]; message.mediaObject webObj; SendMessageToWXReq *req [[SendMessageToWXReq alloc] init]; req.bText NO; req.message message; req.scene (int)scene; [WXApi sendReq:req completion:nil]; } }extern C是关键。C#端通过[DllImport(__Internal)]调用原生方法时要求方法名不被C编译器修饰extern C保证了这一点。如果你的文件扩展名是.mmObjective-C不加extern C会链接失败如果扩展名是.m本来就够但加上没坏处。微信登录的scope参数目前官方文档只推荐snsapi_userinfo它可以拿到用户的openid、unionid和access_token。state字段是防CSRF攻击用的建议服务端生成一个随机值回传时校验。注意setThumbImage的图片大小不能超过32KB超过的话分享卡片会显示异常。我之前处理一张大尺寸截图压了几次都没压到32K以内最终只好裁剪加压缩——这个限制卡得很死必须在分享前处理干净。3.4 注册微信SDK启动时注册也是必要的一步。微信SDK要求在application:didFinishLaunchingWithOptions:里注册registerApp。同样在分类里加- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [WXApi registerApp:wx1234567890abcdef universalLink:https://yourdomain.com/ios/]; return YES; }这里注意universalLink必须跟你开放平台填写的Universal Links前缀一致而且要以/结尾。如果这里填错了登录回调会偶尔成功偶尔失败表现非常诡异。如果你同时在别的渠道比如一键登录也实现了didFinishLaunchingWithOptions注意方法覆盖的问题。UnityAppController的分类方法在运行时是动态合并的多个分类实现同一个方法时只有一个会生效。我处理过一起冲突解决办法是不要重名把微信注册逻辑挪到application:didFinishLaunchingWithOptions里调用一个单独的初始化函数。但如果你用的是最新Unity版本UnityAppController提供了startUnity相关方法你可以把微信注册放在Unity启动完成后的OnUnityReady回调里。4. Unity C#层实现回调管理与业务拆分原生层打通了C#这边就要负责把回调接到游戏业务逻辑上。这一层比较容易被忽略实际上最需要设计。4.1 桥接类WeChatManager创建场景里的一个空GameObject挂上WeChatManager脚本。脚本里定义回调方法方法名必须跟原生层UnitySendMessage的第三个参数对应。using System; using System.Runtime.InteropServices; using UnityEngine; public class WeChatManager : MonoBehaviour { [DllImport(__Internal)] private static extern void _WeChatLogin(); [DllImport(__Internal)] private static extern void _WeChatShareText(string text, int scene); [DllImport(__Internal)] private static extern void _WeChatShareLink(string title, string desc, string url, string thumbImagePath, int scene); private static Actionstring _loginCallback; private static Actionint _shareCallback; public static WeChatManager Instance { get; private set; } private void Awake() { Instance this; DontDestroyOnLoad(gameObject); } public static void Login(Actionstring onLogin) { _loginCallback onLogin; _WeChatLogin(); } public static void ShareText(string text, int scene, Actionint onShare) { _shareCallback onShare; _WeChatShareText(text, scene); } public static void ShareLink(string title, string desc, string url, string thumbImagePath, int scene, Actionint onShare) { _shareCallback onShare; _WeChatShareLink(title, desc, url, thumbImagePath, scene); } public void OnLoginCallback(string result) { // result格式: login_{code}_{errCode} if (result.StartsWith(login_)) { string[] parts result.Split(_); if (parts.Length 3) { string code parts[1]; int errCode int.Parse(parts[2]); _loginCallback?.Invoke(errCode 0 ? code : $ERROR_{errCode}); } } } public void OnShareCallback(string result) { // result格式: share_{errCode} if (result.StartsWith(share_)) { string[] parts result.Split(_); if (parts.Length 2) { int errCode int.Parse(parts[1]); _shareCallback?.Invoke(errCode); } } } }这个设计有几个考量回调入参是string不是对象。因为UnitySendMessage只能传字符串把对象序列化成JSON从一个方向传也是可以的但我为了简单直接用分隔符拼接。如果业务复杂可以考虑用JSON格式传解析更稳。回调处理放在Update之外直接在UnitySendMessage调用的线程里执行。Unity的UnitySendMessage实际上是在主线程执行的可以放心操作Unity API。静态回调变量_loginCallback和_shareCallback在登录/分享发起时赋值、回调后清空可以在调用后做null处理避免跨多次调用的消息串台。4.2 登录后的业务处理微信登录在客户端只能拿到code真正的用户信息openid、nickname、avatar需要用code去服务端换。这是微信SDK的安全设计也是我强烈建议你不要在客户端直接调接口换token的原因。我当时的做法是客户端拿到code后通过我们自己服务器的登录接口传过去。服务器拿着code去微信的https://api.weixin.qq.com/sns/oauth2/access_token这个接口换access_token和openid。再用access_token调/sns/userinfo拿用户资料。服务器处理好后返回带签名的登录凭证给客户端客户端存到本地进入游戏。这个流程里有个坑微信的code有效期只有五分钟而且只能用一次。如果你的服务器处理超时或失败你不能用同一个code重试得让用户重新调起登录。所以多一步异常处理是必须的提示语要友好一点不然用户会觉得登录怎么老失败。分享那边相对简单OnShareCallback拿到errCode后0就是成功-2就是用户取消弹出对应的UI提示即可。4.3 图标与缩略图的处理分享链接时微信要求有缩略图。图上文说过不能超过32KB实际操作中还要注意格式。我测试下来PNG格式的透明图在某些版本微信上会变成黑底建议用JPG。另外imageWithContentsOfFile要求传的是本地路径你要先把图从StreamingAssets或PersistentDataPath拷贝出来不能直接传Application.streamingAssetsPath那样的虚拟路径。public static string PrepareShareThumb(Texture2D tex) { // 等比缩放 int maxSize 100; Texture2D scaled ScaleTexture(tex, maxSize, maxSize); byte[] bytes scaled.EncodeToJPG(80); string path Path.Combine(Application.persistentDataPath, share_thumb.jpg); File.WriteAllBytes(path, bytes); return path; }这里压缩完最好校验一下bytes.Length大于32768就继续降画质或缩尺寸。你不想用户分享时微信弹缩略图过大这种体验很伤。5. 常见问题与排查技巧实录这块是本文的重点全是实打实踩过坑后总结的经验。微信iOS接入的大部分调试时间都花在排查下面几个问题上。5.1 登录回调偶发收不到这是最常见的坑表现是调起微信登录微信界面正常弹出输完密码授权后微信界面消失但游戏里没有任何反应。排查链路从三个方向走第一步确认微信是否真的调用了你的onResp。可以在原生代码里加日志或者直接加NSLog看Xcode控制台有没有输出。如果没有输出说明微信压根不认识你的App多半是registerApp的universalLink填错了或者开放平台配置的Universal Links跟代码对不上。第二步确认onResp执行了但UnitySendMessage没送达。这种情况在onResp里有日志、在C#的OnLoginCallback里打了断点又没执行的时候要检查UnitySendMessage的第一个参数是不是WeChatManager。这个字符串必须跟场景里GameObject的名字完全一致大小写、空格都不能差。很多人把脚本挂在名叫Manager的物体上代码里却写着WeChatManager消息就这样丢了。第三步检查回调是否在App激活前到达。如果用户是冷启动直接被微信通过Universal Links拉起来的比如通过分享链接进入App然后马上登录回调时机可能太早Unity还没准备好接收消息。稳妥的做法是启动时等UnitySendMessage的接收方注册完成再调起微信登录或者在微信回调里加一个小延时比如0.5秒再发消息。5.2 分享卡片显示未验证应用或加载失败这个提示的意思通常是微信认为你这个App不具备分享权限。有几种情况开放平台的应用审核状态不是已审核通过。微信开放平台的规则是新注册的应用默认是审核中状态审核通过后才能拥有完整的分享和登录能力。我第一次调试时就是审核被拒状态一直是审核中结果分享卡片永远显示未验证应用。解决方法只能是去开放平台确认状态该整改的整改。Universal Links的路径规则没匹配上。重新核对apple-app-site-association文件注意微信要求路径中不能有?参数。同一个App ID在开放平台绑定了错误的Bundle ID。这种问题通常在别人接手项目或打包机配置不一致时出现——你本地对了发出去的包不对别人自然跳不起来。5.3 分享缩略图显示为空白我遇到过分享出去好友一看是空白卡片但卡片样式正常跳转也正常。查了半天发现是缩略图路径写错了imageWithContentsOfFile传了一个不存在的路径微信静默加载失败卡片留了标题和描述但没图。解决办法就是我从4.3节开始坚持的那套先把图写到Application.persistentDataPath这个路径对iOS是真实存在的、可写的确认文件存在后再传给原生层。路径最好在回调里加一层Debug.Log打印出来装上真机验证一遍。5.4 微信SDK初始化崩溃如果你在registerApp之前就调了sendReq或者在Unity还没完全启动时就调_WeChatLogin会crash。原因其实很简单微信SDK要求先注册后才能发请求。我在项目启动流程里把登录按钮放在所有初始化完成之后才激活可还是因为第三方登录插件的时序问题提前触发过一次。这种情况建议在C#层做保护private static bool _isReady false; public static void Init() { // 调用完registerApp后再设为true _isReady true; } public static void Login(Actionstring onLogin) { if (!_isReady) { onLogin?.Invoke(ERROR_NOT_READY); return; } _loginCallback onLogin; _WeChatLogin(); }给用户一个微信组件初始化中的提示远好过App闪退体验。5.5 排查工具最后分享一个排查工具链调试微信SDK时非常有用Xcode控制台的NSLog输出。在原生代码里加日志特别是application:openURL:是不是被调到了。safari直接访问https://yourdomain.com/apple-app-site-association看文件是否符合JSON格式且没有跳转。用curl -I看服务器的返回头确认Content-Type是application/json或application/pkix-crl没有经过302跳转。断点调试onResp确认errCode的具体值。微信的错误码有文档-1是系统错误-2是用户取消-4是操作不支持-5是授权拒绝排查时直接看文档对比。6. 实用建议把登录分享做成可持续维护的模块整个流程走通之后有几个工程层面的建议供你参考。建议在Unity侧把微信能力封装成独立模块不跟业务逻辑耦合。比如WeChatManager只负责调起登录/分享和返回结果具体登录后要不要跳转关卡分享成功要不要发奖励这些逻辑放在业务层去订阅回调。这样一来换SDK版本、换第三方渠道都只需要改桥接层业务代码不动。打包机配置要保证统一。微信SDK的集成状态跟Info.plist强相关我出过一次事故开发环境调通了到了发布机因为Info.plist里的LSApplicationQueriesSchemes被热更新脚本覆盖导致正式包调不起微信。后来我索性在Assets/Plugins/iOS下放了一份专门的Info.plist片段用Unity的追加模式合并确保每次出包都带上微信必需的字段。版本管理上微信SDK的版本迭代不算慢建议升级前先去开放平台看更新日志特别是Universal Links相关的改动因为微信对Universal Links的校验规则有过收紧。我有一个习惯每次升级完SDK就在真机上完整跑一遍登录分享被回调三个链路不要只看编译通过就放行。从研发效率的角度说微信登录分享这种能力尽早接入、尽早调通比较好。因为它牵扯开放平台审核、iOS签名、Universal Links等多个外部依赖任何一个环节都需要等审核或者等配置生效越往后拖越容易让发版流程卡壳。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →