| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- #import "GoogleWrapper.h"
- #import <UIKit/UIKit.h>
- #import <GoogleSignIn/GoogleSignIn.h>
- #import "platform/apple/JsbBridgeWrapper.h"
- #include "application/ApplicationManager.h"
- #include "application/BaseApplication.h"
- #include "base/Scheduler.h"
- #include "engine/BaseEngine.h"
- @interface GoogleWrapper ()
- @property(nonatomic) BOOL initialized;
- @property(nonatomic) BOOL loginPending;
- @end
- @implementation GoogleWrapper
- + (instancetype)shared {
- static GoogleWrapper *instance;
- static dispatch_once_t once;
- dispatch_once(&once, ^{ instance = [GoogleWrapper new]; });
- return instance;
- }
- - (void)registerBridge {
- // Must be capture-free: Creator 3.8.8's MRC bridge releases this block.
- static OnScriptEventListener const listener = ^(NSString *payload) {
- dispatch_async(dispatch_get_main_queue(), ^{ [[GoogleWrapper shared] handlePayload:payload]; });
- };
- [[JsbBridgeWrapper sharedInstance] addScriptEventListener:@"google.request" listener:listener];
- }
- - (BOOL)handleURL:(NSURL *)url { return [GIDSignIn.sharedInstance handleURL:url]; }
- - (void)reply:(NSDictionary *)request data:(NSDictionary *)data cancelled:(BOOL)cancelled error:(NSString *)error {
- NSMutableDictionary *response = [@{@"requestId": request[@"requestId"], @"method": request[@"method"],
- @"success": @(!error), @"cancelled": @(cancelled)} mutableCopy];
- if (data) response[@"data"] = data;
- if (error) response[@"error"] = @{@"code": @"GOOGLE_IOS_ERROR", @"message": error};
- NSData *json = [NSJSONSerialization dataWithJSONObject:response options:0 error:nil];
- NSString *payload = [[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding];
- auto app = CC_CURRENT_APPLICATION();
- if (!app || !app->getEngine() || !payload) return;
- app->getEngine()->getScheduler()->performFunctionInCocosThread([payload]() {
- [[JsbBridgeWrapper sharedInstance] dispatchEventToScript:@"google.response" arg:payload];
- });
- }
- - (void)handlePayload:(NSString *)payload {
- id decoded = [NSJSONSerialization JSONObjectWithData:[payload dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
- if (![decoded isKindOfClass:NSDictionary.class]) return;
- NSDictionary *request = decoded;
- if (![request[@"requestId"] isKindOfClass:NSString.class] || ![request[@"method"] isKindOfClass:NSString.class]) return;
- NSString *method = request[@"method"];
- @try {
- if (self.loginPending) {
- [self reply:request data:nil cancelled:NO error:@"Google 登录仍在进行中。"];
- } else if ([method isEqualToString:@"init"]) {
- NSDictionary *params = [request[@"params"] isKindOfClass:NSDictionary.class] ? request[@"params"] : @{};
- NSString *clientID = [NSBundle.mainBundle objectForInfoDictionaryKey:@"GIDClientID"];
- NSString *serverID = params[@"webClientId"];
- if (![clientID isKindOfClass:NSString.class] || ![clientID hasSuffix:@".apps.googleusercontent.com"] ||
- [clientID containsString:@"YOUR_"] || ![serverID isKindOfClass:NSString.class] ||
- ![serverID hasSuffix:@".apps.googleusercontent.com"]) {
- [self reply:request data:nil cancelled:NO error:@"请配置 Info.plist 的 GIDClientID(iOS)及 AppConfig.GoogleID(Web)。"];
- return;
- }
- NSArray *parts = [[clientID componentsSeparatedByString:@"."] reverseObjectEnumerator].allObjects;
- NSString *scheme = [parts componentsJoinedByString:@"."];
- BOOL found = NO;
- for (NSDictionary *type in [NSBundle.mainBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"]) {
- if ([type[@"CFBundleURLSchemes"] containsObject:scheme]) found = YES;
- }
- if (!found) {
- [self reply:request data:nil cancelled:NO error:@"Info.plist 缺少 Google iOS Client ID 对应的反向 URL Scheme。"];
- return;
- }
- GIDSignIn.sharedInstance.configuration = [[GIDConfiguration alloc] initWithClientID:clientID serverClientID:serverID];
- self.initialized = YES;
- [self reply:request data:@{} cancelled:NO error:nil];
- } else if (!self.initialized) {
- [self reply:request data:nil cancelled:NO error:@"Google SDK 未初始化。"];
- } else if ([method isEqualToString:@"logout"]) {
- [GIDSignIn.sharedInstance signOut];
- [self reply:request data:@{} cancelled:NO error:nil];
- } else if ([method isEqualToString:@"login"]) {
- UIViewController *presenter = UIApplication.sharedApplication.delegate.window.rootViewController;
- while (presenter.presentedViewController) presenter = presenter.presentedViewController;
- if (!presenter || UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
- [self reply:request data:nil cancelled:NO error:@"请在游戏前台且系统授权弹窗关闭后重试 Google 登录。"];
- return;
- }
- self.loginPending = YES;
- [GIDSignIn.sharedInstance signInWithPresentingViewController:presenter completion:^(GIDSignInResult *result, NSError *error) {
- dispatch_async(dispatch_get_main_queue(), ^{
- self.loginPending = NO;
- BOOL cancelled = [error.domain isEqualToString:kGIDSignInErrorDomain] && error.code == kGIDSignInErrorCodeCanceled;
- if (error) {
- [self reply:request data:nil cancelled:cancelled error:cancelled ? nil : error.localizedDescription];
- return;
- }
- GIDGoogleUser *user = result.user;
- if (!user.idToken.tokenString.length) {
- [self reply:request data:nil cancelled:NO error:@"Google 未返回 ID Token。"];
- return;
- }
- [self reply:request data:@{@"idToken": user.idToken.tokenString,
- @"userId": user.userID ?: @"", @"displayName": user.profile.name ?: @"",
- @"email": user.profile.email ?: @""} cancelled:NO error:nil];
- });
- }];
- } else {
- [self reply:request data:nil cancelled:NO error:@"不支持的 Google 方法。"];
- }
- } @catch (NSException *exception) {
- if ([method isEqualToString:@"login"]) self.loginPending = NO;
- [self reply:request data:nil cancelled:NO error:exception.reason ?: @"Google SDK 调用失败。"];
- }
- }
- @end
|