Bug表现
服务器:海外服
平台:iOS
项目临近发布前测试发现的,具体表现为在iOS设备上,使用TapTap登录,输入完账号密码后点击登录游戏无反应,再次点击需要再次登录。
排查过程
查找过程中发现成功拉起TapTap登录没有异常,但是在TapTap上点击登录后游戏没有收到任何回调。
正常情况下,点击登录之后SDK会调用对应的回调方法,包括:
登录成功(OnLoginSuccess)
登录失败(OnLoginError)
登录取消(OnLoginCancel)
最终在TapSDK的同学帮助下定位了问题,本应由TapSDK传给客户端的消息被AppsFlyer拦截了。。
相关AppsFlyer代码如下:
// AppsFlyerAppController.mm
// Unity-iPhone
//
// Created by Jonathan Wesfield on 30/07/2019.
//
#import <Foundation/Foundation.h>
#import "UnityAppController.h"
#import "AppDelegateListener.h"
#import "AppsFlyeriOSWrapper.h"
#if __has_include(<AppsFlyerLib/AppsFlyerLib.h>)
#import <AppsFlyerLib/AppsFlyerLib.h>
#else
#import "AppsFlyerLib.h"
#endif
#import <objc/message.h>
/**
Note if you would like to use method swizzeling see AppsFlyer+AppController.m
If you are using swizzeling then comment out the method that is being swizzeled in AppsFlyerAppController.mm
Only use swizzeling if there are conflicts with other plugins that needs to be resolved.
*/
@interface AppsFlyerAppController : UnityAppController <AppDelegateListener>
{
BOOL didEnteredBackGround;
}
@end
@implementation AppsFlyerAppController
- (instancetype)init
{
self = [super init];
if (self) {
id swizzleFlag = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppsFlyerShouldSwizzle"];
BOOL shouldSwizzle = swizzleFlag ? [swizzleFlag boolValue] : NO;
if(!shouldSwizzle){
UnityRegisterAppDelegateListener(self);
}
}
return self;
}
- (void)didFinishLaunching:(NSNotification*)notification {
NSLog(@"got didFinishLaunching = %@",notification.userInfo);
if (_AppsFlyerdelegate == nil) {
_AppsFlyerdelegate = [[AppsFlyeriOSWarpper alloc] init];
}
[[AppsFlyerLib shared] setDelegate:_AppsFlyerdelegate];
if (notification.userInfo[@"url"]) {
[self onOpenURL:notification];
}
}
-(void)didBecomeActive:(NSNotification*)notification {
NSLog(@"got didBecomeActive(out) = %@", notification.userInfo);
if (didEnteredBackGround == YES && AppsFlyeriOSWarpper.didCallStart == YES) {
[[AppsFlyerLib shared] start];
didEnteredBackGround = NO;
}
}
- (void)didEnterBackground:(NSNotification*)notification {
NSLog(@"got didEnterBackground = %@", notification.userInfo);
didEnteredBackGround = YES;
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler {
[super application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
[[AppsFlyerAttribution shared] continueUserActivity:userActivity restorationHandler:restorationHandler];
return YES;
}
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
NSLog(@"got openUrl: %@",url);
[super application:application openURL:url options:options];
[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
return NO;
}
- (void)onOpenURL:(NSNotification*)notification {
NSLog(@"got onOpenURL = %@", notification.userInfo);
NSURL *url = notification.userInfo[@"url"];
NSString *sourceApplication = notification.userInfo[@"sourceApplication"];
if (sourceApplication == nil) {
sourceApplication = @"";
}
if (url != nil) {
[[AppsFlyerAttribution shared] handleOpenUrl:url sourceApplication:sourceApplication annotation:nil];
}
}
- (void)didReceiveRemoteNotification:(NSNotification*)notification {
NSLog(@"got didReceiveRemoteNotification = %@", notification.userInfo);
[[AppsFlyerLib shared] handlePushNotification:notification.userInfo];
}
@end
IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController)
/**
Note if you would not like to use IMPL_APP_CONTROLLER_SUBCLASS you can replace it with the code below.
<code>
+(void)load
{
[AppsFlyerAppController plugin];
}
// Singleton accessor.
+ (AppsFlyerAppController *)plugin
{
static AppsFlyerAppController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[AppsFlyerAppController alloc] init];
});
return sharedInstance;
}
</code>
**/ 首先TapTap的跳转登录是通过修改UnityAppController中的openURL等相关函数实现的;
而该SDK通过 IMPL_APP_CONTROLLER_SUBCLASS(AppsFlyerAppController) 将自己的AppController文件替换掉了原本的UnityAppController,导致原先TapTap SDK的代码没有生效,以至于无法接收到登录回调,最后无法登录。。
解决方法
- 删除AppsFlyer;
- 在AppsFlyer的AppController中调用原UnityAppController的函数;
这两种方法都可以,国内版不需要AppsFlyer所以可以删掉。但是海外版还是需要该SDK的。
考虑到该AppController类继承自UnityAppController,最终采用了方法2,以openURL函数为例:
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options {
NSLog(@"got openUrl: %@",url);
[super application:application openURL:url options:options];
[[AppsFlyerAttribution shared] handleOpenUrl:url options:options];
return NO;
} 增加 [super application:application openURL:url options:options]; 语句,重新调用了父类UnityAppController的openURL函数,使得游戏可以正常接受来自TapSDK的信息,成功登录。
同时,假如以后不需要该SDK了,可以直接删除而不用修改代码(删除之后会默认使用原有的UnityAppController)。
反思
我的锅。。其实以我的计划来说,AppsFlyer已经接入游戏,但还没有经过测试,这次出版本来不及,所以我加了开关给关掉了。
可是万万没想到AppsFlyer SDK还有替换AppController这一手,我接了那么多SDK第一次见,还是我太naive了。。。


