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了。。。