前言
第一次在 IOS 上接入 Unity 项目的SDK
在此记录系统性的总结一下自己接入过程,避免以后踩同样的坑
开发环境
- Xcode 12.5
- Unity 2018.4.10f
开发语言
- C#
- OC (因为 C# 与 OC 交互起来比较方便)
一、IOS 与 Unity 的交互
1. 接入前须知
在 Unity 项目中的 Plugins->IOS 中编写的代码  会在打包的时候一同带入 Xcode 项目中的 Libraries->Plugins->IOS 文件夹下
 下文所有要编写的代码都要编写在其中
2. Unity 调用 IOS 中的方法
C# 无法直接调用 OC 或者 Swift 当中的方法
但是可以利用C#的特性来访问C语言所定义的接口
然后再通过C接口再调用ObjC的代码(对于Swift代码则还需要使用OC桥接)
a. 在 Plugins->IOS 编写 Test.mm 文件
该方法的作用是将传入的 message 在 Xcode 的调试中输出
extern "C"{
void IOSLog(const char *message);
}
void IOSLog(const char *message){
NSString* str = [[NSString alloc]initWithUTF8String:message];
NSLog(@"%@", str);
}
b. 在 Unity 的脚本中引入声明
#if UNITY_IOS
[DllImport("__Internal")]
static extern void IOSLog(string message);
#endif
声明为静态变量方便调用
注:如果找不到该方法,可能导致编译失败
c. 在 Unity 中调用
IOSLog("Hello world!");
3. IOS 调用 Unity 中的方法
IOS 调用 Unity 的方法和 Android 一致
使用 UnitySendMessage 方法
UnitySendMessage(String var0, String var1, String var2)
具体可以查看 Android 调用 Unity 方法
二、UnityAppController
Unity 打出来的包中会包含一个 UnityAppController.mm 文件
这个文件与 Android 中的 UnityPlayerActivity.java 类似
是程序的入口,许多重要的生命周期函数都在这里实现
由于每次打包的时候都会重新生成该文件
所以我们需要自己写一个 MyUnityAppController.mm 并继承它
#import "UnityAppController.h"
#import "AgentLogic.h"
@interface MyUnityAppController : UnityAppController
@end
IMPL_APP_CONTROLLER_SUBCLASS (MyUnityAppController)
@implementation MyUnityAppController
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application:application didFinishLaunchingWithOptions:launchOptions];
NSLog(@"MyUnityAppController");
return YES;
}
@end
注:MyUnityAppController.mm 可以放在上文提到的 IOS 文件夹下
三、微信 SDK 接入
最重要的还是要按照 微信开放平台 上的文档进行操作
1. 配置 Universal Links (通用链接)
什么是 Universal Links ?
Universal Link 是 Apple 在 iOS 9 推出的一种能够方便的通过传统 HTTPS 链接来启动 APP的功能。如果你的应用支持 Universal Link,当用户点击一个链接时可以跳转到你的网站并获得无缝重定向到对应的 APP,且不需要通过Safari 浏览器。如果你的应用不支持的话,则会在 Safari 中打开该链接。
如何支持 Universal Links
一个支持 HTTPS 的域名,并且拥有该域名下上传到根目录的权限(为了上传Apple指定文件)
a. 在苹果开发者中心配置 Associated Domains
 b. 根据微信文档创建配置文件 apple-app-site-association
创建一个内容为json格式的文件
当用户安装你的应用的时候,同时会下载该文件
这个文件名必须为apple-app-site-association,切记没有后缀名
文件在通过 QQ 等文件传输之后可能会自己加上后缀名,要留意
{
"applinks": {
"apps": [],
"details": [
{
"appID": "8P7343TG54.com.tencent.xin.SDKSample",
"paths": ["/sdksample/*"]
}
]
}
}
c. 上传文件
配置好文件后,就可以将文件上传到域名所对应的 根目录 或者 .well-known 目录下
并试着是否能访问到该文件
d. 在微信开放平台里配置 Universal Links
根据你在 apple-app-site-association 中的配置
在微信上应该注册为 https://xxx.xxx.xxx/sdksample/
e. 在项目中配置域名
在项目的 Signing & Capabilities 的 Associated Domains 中添加你的域名
如果没有该选项可以添加

2. 搭建开发环境
这里还是要提一下,一定要严格按照官方文档来!
a. 将 SDK 中包含的文件加入到项目当中
推荐一同放入 IOS 文件夹中
分别是 WXApi.h、WXApiObject.h、libWeChatSDK.a

b. 添加相应的库
Security.framework, CoreGraphics.framework, WebKit.framework
c. 配置 Build Setting
在工程文件中选择 Build Setting
在"Other Linker Flags"中加入"-ObjC -all_load"
在 Search Paths 中添加 libWeChatSDK.a ,WXApi.h,WXApiObject.h

d. 添加 URL scheme
选择工程设置项,选中 TARGETS 一栏
在 info 标签栏的 URL type 添加 URL scheme 为你所注册的应用程序 id

e. 添加 LSApplicationQueriesSchemes
选择工程设置项,选中 TARGETS 一栏
在 info 标签栏的 LSApplicationQueriesSchemes 添加 “weixin” 和 “weixinULAPI”

3. 在代码中使用
以微信为例子,后续接入多个 SDK 的时候都可以按照这个方法来
根据官方文档,在 UnityAppController 的各个生命周期里调用相应的函数
这里要注意重写 AppDelegate 或 SceneDelegate 的 continueUserActivity 方法
注意:适配了SceneDelegate的App,系统将会回调SceneDelegate的continueUserActivity方法,所以需要重写SceneDelegate的该方法。
这里建议将微信需要调用的方法都封装到一起
4. 封装微信 api
编写 AgentLoic.h 继承 WXApiDelegate 接口
可以将要实现的方法都写在里面
#import "WXApi.h"
@interface AgentLogicInterface : NSObject<WXApiDelegate>
@end
class AgentLogic
{
public:
AgentLogic();
virtual ~AgentLogic();
static AgentLogic* getInstance();
void didFinishLaunchingWithOptions(UIApplication *app, NSDictionary *launchOptions);
bool continueUserActivity(UIApplication *app, NSUserActivity *userActivity);
bool handleOpenURL(UIApplication* app, NSURL* url);
bool openURL(UIApplication* app, NSURL* url, NSString* sourceApplication, id annotation);
public:
void onWXReq(BaseReq* req);
void onWXResp(BaseResp* resp);
void WXShareImage(char *bytes, int type, int length);
private:
AgentLogicInterface* m_pInterface;
};
并在 AgentLogic.mm 实现对应方法
#import "AgentLogic.h"
extern "C"{
void WeChatShareImage(char *bytes, int type, int length);
void IOSLog(const char *message);
}
#pragma mark SDK
AgentLogic* g_pAgentLogic = NULL;
AgentLogic* AgentLogic::getInstance(){
if (g_pAgentLogic == NULL){
g_pAgentLogic = new AgentLogic();
}
return g_pAgentLogic;
}
AgentLogic::AgentLogic()
{
m_pInterface = [AgentLogicInterface new];
}
AgentLogic::~AgentLogic()
{
}
void AgentLogic::didFinishLaunchingWithOptions(UIApplication *app, NSDictionary *launchOptions){
[WXApi registerApp:@"123456789" universalLink:@"https://xxx.xxx.xxx/sdksample/"];
}
bool AgentLogic::continueUserActivity(UIApplication *app, NSUserActivity *userActivity){
return [WXApi handleOpenUniversalLink:userActivity delegate:m_pInterface];
}
bool AgentLogic::handleOpenURL(UIApplication* app, NSURL* url)
{
return [WXApi handleOpenURL:url delegate:m_pInterface];
}
bool AgentLogic::openURL(UIApplication* app, NSURL* url, NSString* sourceApplication, id annotation)
{
return [WXApi handleOpenURL:url delegate:m_pInterface];
}
#pragma mark 微信
void AgentLogic::onWXResp(BaseResp *resp)
{
if([resp isKindOfClass:[SendMessageToWXResp class]])
{
SendMessageToWXResp *sendResp = (SendMessageToWXResp *)resp;
std::string strParam = "";
if(sendResp.errCode == 0)
{
NSLog(@"#####分享成功");
}
else
{
NSLog(@"#####分享失败");
}
}
}
void AgentLogic::onWXReq(BaseReq *req)
{
NSLog(@"#####onWXReq");
}
void WeChatShareImage(char *bytes, int type, int length){
SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
req.bText = NO;
req.scene = type;
NSData *imageData = [NSData dataWithBytes:bytes length:length];
UIImage *image = [UIImage imageWithData:imageData];
CGSize newSize = CGSizeMake(128, 128);
UIGraphicsBeginImageContext(newSize);
[image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
UIImage* thumbImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
WXImageObject *imageObject = [WXImageObject object];
imageObject.imageData = imageData;
WXMediaMessage *message = [WXMediaMessage message];
message.thumbData = UIImageJPEGRepresentation(thumbImage, 0.5);
message.mediaObject = imageObject;
req.message = message;
[WXApi sendReq:req completion:nil];
}
void IOSLog(const char *message){
......
}
实现 WXApiDelegate 接口,调用相应的方法
@implementation AgentLogicInterface
#pragma mark 微信接口
-(void)onResp:(BaseResp *)resp
{
AgentLogic::getInstance()->onWXResp(resp);
}
-(void)onReq:(BaseReq *)req
{
AgentLogic::getInstance()->onWXReq(req);
}
@end
最后在 MyUnityAppController.mm 中调用
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
[super application:application didFinishLaunchingWithOptions:launchOptions];
AgentLogic::getInstance()->didFinishLaunchingWithOptions(application, launchOptions);
return YES;
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
[super application:application handleOpenURL:url];
return AgentLogic::getInstance()->handleOpenURL(application, url);
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[super application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
return AgentLogic::getInstance()->openURL(application, url, sourceApplication, annotation);
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray<id<UIUserActivityRestoring>> * __nullable restorableObjects))restorationHandler {
return AgentLogic::getInstance()->continueUserActivity(application, userActivity);
}
注:在 continueUserActivity 方法中,不需要调用父类的方法,否则会导致运行报错。
|