官方教程:iOS接入指南 | 微信开放文档
我是用cocoapods集成的,因为手动集成老有下面这个问题,一时解决不了
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[WXApi genExtraUrlByReq:withAppData:]: unrecognized selector sent to class
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[WXApi genExtraUrlByReq:withAppData:]: unrecognized selector sent to class
一、安装cocoapods
1、安装Homebrew
国内源:HomebrewCN: Homebrew 国内安装脚本,快速部署 brew ,国内镜像
这个源真的很棒,安装很快。
安装Homebrew
/bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
2、安装cocoapods
brew install cocoapods
二、接入微信登录
1、下载微信sdk
前往下载基本信息 | 微信开放文档,打开压缩文件
我把三个文件放在Plugins/iOS/文件夹下的同一个文件夹内了
2、编写ObjectiveC代码调用微信api
创建WeChatUnity.mm、WXApiManager.h、WXApiManager.mm这三个文件,文件也放在Plugins/iOS/文件夹下
代码内容如下,参考网上大佬的:
WeChatUnity.mm
// WeChatUnity.mm
#import <Foundation/Foundation.h>
#import "WXApiManager.h"
#import <UIKit/UIKit.h>
#import <WXApi.h>
#define UNITY_CS_API extern "C"
static NSString *mWXAppid = nil;
// 将c字符串const char* 转为 oc字符串NSString
static inline NSString * str_c2ns(const char*s)
{
if (s)
return [NSString stringWithUTF8String: s];
else
return [NSString stringWithUTF8String: ""];
}
// 初始化
UNITY_CS_API void WeChatInit(const char* appId, const char* universalLink)
{
NSLog(@"WeChatInit");
//在register之前打开log, 后续可以根据log排查问题,第一次测试sdk时可以取消注释
// [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString *log) {
// NSLog(@"WeChatSDK: %@", log);
// }];
// 向微信注册
mWXAppid = str_c2ns(appId);
NSString *link = str_c2ns(universalLink);
NSLog(@"init: %@ %@", mWXAppid, link);
[WXApi registerApp:mWXAppid universalLink:link];
// 调用自检函数,第一次测试sdk时可以取消注释
// [WXApi checkUniversalLinkReady:^(WXULCheckStep step, WXCheckULStepResult* result) {
// NSLog(@"%@, %u, %@, %@", @(step), result.success, result.errorInfo, result.suggestion);
// }];
}
// WeChatUnity.mm
UNITY_CS_API void WeChatLogin(const char* state)
{
NSLog(@"WeChatLogin");
SendAuthReq* req = [[SendAuthReq alloc] init];
req.scope = @"snsapi_userinfo";;
req.state = str_c2ns(state);
[WXApi sendReq:req completion:nil];
// 此时会拉起微信,授权后会回调WXApiManager的onResp方法
}
WXApiManager.h
// WXApiManager.h
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "WXApi.h"
// @end
@interface WXApiManager : UIResponder<UIApplicationDelegate, WXApiDelegate>
@property (strong, nonatomic) UIWindow *window;
+ (instancetype)sharedManager;
@end
WXApiManager.mm
#import "WXApiManager.h"
@implementation WXApiManager
// 单例
+(instancetype)sharedManager {
static dispatch_once_t onceToken;
static WXApiManager *instance;
dispatch_once(&onceToken, ^{
instance = [[WXApiManager alloc] init];
});
return instance;
}
// WXApiManager.mm
- (void)onResp:(BaseResp *)resp {
if ([resp isKindOfClass:[SendAuthResp class]]) {
NSLog(@"微信授权回调");
if (resp.errCode == 0) {
UnitySendMessage("UnityAndroidConnector", "WeChatLoginSuccess", ((SendAuthResp *)resp).code.UTF8String);
}
else
{
// 失败,回调给Unity
NSString *stringNumber = [NSString stringWithFormat:@"%d", resp.errCode];
UnitySendMessage("UnityAndroidConnector", "WeChatLoginFail", stringNumber.UTF8String);
}
}
}
- (void)onReq:(BaseReq *)req {
// TODO 微信回调,从微信端主动发送过来的请求
}
@end
创建xxxAppController.mm文件,我这里叫CustomAppController,也放在Plugins/iOS/文件夹下
CustomAppController.mm,内容如下
// CustomAppController.mm
#import "WXApiManager.h"
#import "UnityAppController.h"
@interface CustomAppController : UnityAppController
@end
IMPL_APP_CONTROLLER_SUBCLASS (CustomAppController)
@implementation CustomAppController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application:application didFinishLaunchingWithOptions:launchOptions];
// 可以在这里自动注册微信,也可以在手动调用WeChatUnity.mm的WeChatInit进行注册
// [WXApi registerApp:@"wx123123123" universalLink:@"https://xxx.com/app/"];
return YES;
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler
{
return [WXApi handleOpenUniversalLink:userActivity delegate:[WXApiManager sharedManager]];
}
@end
3、编写Unity代码调用和处理回调
调用方法如下:
void iOSInit()
{
WeChatInit("wx12312312", "https://www.xxx.com/app/");
}
void iOSLogin()
{
WeChatLogin("app_wechat");
}
[DllImport("__Internal")]
static extern void WeChatInit(string appId, string universalLink);
[DllImport("__Internal")]
static extern void WeChatLogin(string state);
回调处理:UnityAndroidConnector.cs
public class UnityAndroidConnector : MonoSingleton<UnityAndroidConnector>
{
public void WeChatLoginSuccess(string code)
{
// 传给服务器
}
public void WeChatLoginFail(string codestr)
{
int code = int.Parse(codestr);
// 错误码处理
}
}
WXApiManager.mm中的UnitySendMessage方法里的参数就对应UnityAndroidConnector.cs里的方法。
记得:UnityAndroidConnector.cs需要挂载在名为UnityAndroidConnector的组件上,即组件名和脚本名保持一致,这个名字你也可以取你喜欢的
UnitySendMessage("UnityAndroidConnector", "WeChatLoginSuccess", ((SendAuthResp *)resp).code.UTF8String);
UnitySendMessage("UnityAndroidConnector", "WeChatLoginFail", stringNumber.UTF8String);
三、打包
1、引入External Dependency Manager for Unity
打开设置,Assets--External Dependency Manager--iOS Resolver--Settings,设置我这边用的是默认的,然后 Always add the main target to Podfile 这个设置取消勾选(不然会在Build Phases--Link Binary With Libraries里添加上一个Pod_xxxx_framework,打包时会报错,需要手动删除)
2、创建xxxDependencies.xml
在任意Editor文件夹下创建一个xxxDependencies.xml,用于cocoapods打包用,我这里是WechatDependencies.xml
WechatDependencies.xml
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<iosPods>
<iosPod name="WechatOpenSDK-XCFramework" bitcodeEnabled="true" > </iosPod>
</iosPods>
</dependencies>
3、xcode配置
直接用脚本配置xcode配置项,该脚本也需要放在任意Editor文件夹下
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
class iOSBuildTool : IPostprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPostprocessBuild(BuildReport report)
{
if (report.summary.platform == BuildTarget.iOS)
{
string projectPath = report.summary.outputPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject pbxProject = new PBXProject();
pbxProject.ReadFromFile(projectPath);
//Disabling Bitcode on all targets
//Main
string target = pbxProject.GetUnityMainTargetGuid();
pbxProject.SetBuildProperty(target, "ENABLE_BITCODE", "NO");
pbxProject.SetBuildProperty(target, "CONFIGURATION", "Release");
pbxProject.SetBuildProperty(target, "CONFIGURATION_BUILD_DIR", "$(BUILD_DIR)/$(CONFIGURATION)");
pbxProject.SetBuildProperty(target, "OTHER_LDFLAGS", "-ObjC -all_load -lstdc++ -lsqlite3");
// 添加framework
pbxProject.AddFrameworkToProject(target, "Security.framework", false);
pbxProject.AddFrameworkToProject(target, "CoreGraphics.framework", false);
pbxProject.AddFrameworkToProject(target, "WebKit.framework", false);
//Unity Tests
string targetGuid = pbxProject.TargetGuidByName(PBXProject.GetUnityTestTargetName());
pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO");
//Unity Framework
string frameworkTarget = pbxProject.GetUnityFrameworkTargetGuid();
pbxProject.SetBuildProperty(frameworkTarget, "ENABLE_BITCODE", "NO");
pbxProject.SetBuildProperty(frameworkTarget, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "NO");
// 写入
pbxProject.WriteToFile(projectPath);
// 修改Info.plist文件
var plistPath = report.summary.outputPath + "/Info.plist";
var plist = new PlistDocument();
plist.ReadFromFile(plistPath);
PlistElementDict rootDict = plist.root;
// example of adding a boolean key...
//设置LSApplicationQueriesSchemes(数组)
PlistElementArray loginChannelsArr;
loginChannelsArr = rootDict.CreateArray("LSApplicationQueriesSchemes");
loginChannelsArr.AddString("weixin");
loginChannelsArr.AddString("weixinULAPI");
loginChannelsArr.AddString("weixinURLParamsAPI");
// 添加 url scheme
PlistElementArray urlTypes = rootDict.CreateArray("CFBundleURLTypes");
PlistElementDict wxUrl = urlTypes.AddDict();
wxUrl.SetString("CFBundleTypeRole", "Editor");
wxUrl.SetString("CFBundleURLName", "weixin");
PlistElementArray wxUrlScheme = wxUrl.CreateArray("CFBundleURLSchemes");
wxUrlScheme.AddString("wx12312312");
// 是否加密
rootDict.SetBoolean("ITSAppUsesNonExemptEncryption", false);
// 麦克风权限
rootDict.SetString("NSMicrophoneUsageDescription", "允许访问您的麦克风,以便您能够在xxxx中使用语音功能");
// 应用修改
plist.WriteToFile(plistPath);
//xxx是项目得名称,一定要修改成对应的项目名称
ProjectCapabilityManager projectCapabilityManager = new ProjectCapabilityManager(projectPath, "Entitlements.entitlements", "Unity-iPhone");
// 添加关联域名
projectCapabilityManager.AddAssociatedDomains(new string[] {
"applinks:www.xxx.com",
});
// 添加通知
// projectCapabilityManager.AddPushNotifications(true);
projectCapabilityManager.WriteToFile();
}
}
}
#endif
4、Unity--Build Settings--Build
打包后运行.xcworkspace这个工程,别运行xcodeproj那个工程,然后真机测试。
大致流程应该就这些,有问题就参考一下官方辣鸡教程或者其他大佬的方法。
关于Universal Link的说明
微信开发者后台需要配置Universal Link,一般格式为https://xxx.com/yyy/,xxx.com就是你的域名,yyy就是path
同时需要在你的域名根目录下或者.well-known目录下有一个apple-app-site-association文件,大致内容如下,内容格式为json格式,但文件不带任何后缀名(即你可以先创建一个json文件,保存好后删除文件名里的'.json'后缀)。
{
"applinks": {
"apps": [],
"details": [
{
"appID": "teamid.bundle_id",
"paths": ["/app/*"]
}
]
}
}
teamid可在apple开发者后台拿到,网上搜一下就知道了;bundle_id就是你Unity项目的包名com.xxx.xxxx;paths里的app就对应上面说的app,后面记得加一个‘*’号。
还和Universal Link有关的就是上面提到的WeChatUnity.mm和CustomAppController.mm里的微信注册,那个universalLink的值就是Universal Link,即https://xxx.com/app/,而xcode配置脚本里的applink则是域名,即www.xxx.com,别搞错了。
在安装了你的app的手机safari浏览器地址输入你的Universal Link加随机码(如https://xxx.com/app/abc),转到这个链接时,顶部应该是能出现打开你的app的下拉窗口的,这就表示你的Universal Link配置正确了。