// // XenonSDK.m // XenonSDK // // Created by SAGESSE on 2019/3/11. // Copyright © 2019 SAGESSE. All rights reserved. // #import "XenonSDK.h" #import #import #if SDK_HAS_IDFA #import #endif #import "BubbleView.h" #import "RootViewController.h" #import "NoticeViewController.h" #import "UserBindPhoneViewController.h" #import "UserLoginFastViewController.h" #import "UserLoginPhoneViewController.h" #import "UserCenterViewController.h" #import "XSUtils.h" #import @interface XenonSDK () @end /// /// Game SDK /// @implementation XenonSDK @synthesize user = _user; @synthesize token = _token; /// /// A sington instance of SDK. /// + (instancetype)sharedSDK { static id sharedSDK = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedSDK = XenonSDK.new; if (XSNetwork.baseURL == nil) { XSNetwork.baseURL = @"https://app.funcheergame.com"; } }); return sharedSDK; } /// /// Init the sdk with application code. /// \param parameter The parameter provided by the platform. /// /// \param complete This closure called when init complete. /// - (void)initWithParameter:(NSString*)parameter complete:(void (^)(NSError* error))complete { // If init is complete, ignore it this call. if (self.configuration != nil) { if (complete != nil) { complete(nil); // sdk is ready. } return; } // Parse pre-configuration for parameter. NSArray* configs = [parameter componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"|&"]]; if (configs.count != 4) { if (complete != nil) { complete([XSNetwork errorWithCode:-1 message:@"参数错误"]); // error -1 } return ; } // Update pre-configuration for parameter. self.appId = configs[0]; self.channelId = configs[1]; self.adId = configs[2]; self.adFlag = configs[3]; // Update system configration for server. [XSNetwork configureWithIdentifier:self.adId flags:self.adFlag complete:^(XSConfiguration* object, NSError *error) { // If an error occurs, directly callback cp. if (error != nil) { if (complete != nil) { complete(error); } return; } // An unknown error. if (object.closeState != 0) { // Display an system notice. id qq = self.configuration.fixLinkQQ ?: @""; id tel = self.configuration.fixLinkTel ?: @""; // Set the update title & contents. id contents = [NSString stringWithFormat:@"\n亲爱的玩家您好, 由于服务器紧急维护, 目前游戏暂时无法进入, 给您带来的不便我们深表歉意!\n如果您还有其他问题请与我们联系!\n\n客服QQ: %@\n客服电话: %@", qq, tel]; UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"系统公告" message:contents preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"我知道了" style:UIAlertActionStyleDefault handler:nil]]; self.rootViewController = alert; complete([XSNetwork errorWithCode:-8 message:nil]); return; } // Setup sdk with server configuration. self.configuration = object; // Get third-party platform parameters. if (self.configuration.thirdPartyPrams.length != 0) { NSDictionary* thirdPartyPrams = [NSJSONSerialization JSONObjectWithData:[self.configuration.thirdPartyPrams dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil]; // If the tracker is turned on, create the object. int mask = 0; NSString* dps = thirdPartyPrams[@"dp"]; if (dps.length != 0) { sscanf(dps.UTF8String, "%x", &mask); if (mask != 0) { XSTracker.shared = [XSTracker new]; XSTracker.shared.mask = mask; [XSNetwork trigger:20]; } } // In the initialization. [XSTracker.shared trace:0x010000 parameters:[NSString stringWithFormat:@"%@", NSProcessInfo.processInfo.environment]]; // If an advertisement parameter is specified, try to parse advertising parameter. NSString* adPrams = self.configuration.adPrams; if (adPrams.length != 0) { // Parse advertising parameter. [[adPrams componentsSeparatedByString:@"&"] enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { // Value must be `key=value` NSArray* tmp = [obj componentsSeparatedByString:@"="]; if (tmp.count != 2) { return; } if ([tmp[0] isEqualToString:@"td_app_id"]) { self.configuration.tdAppId = tmp[1]; } if ([tmp[0] isEqualToString:@"td_game_id"]) { self.configuration.tdGameId = tmp[1]; } }]; // If td game id is not provided, the TD sdk is not initialized. if (self.configuration.tdAppId.length != 0 && self.configuration.tdGameId.length != 0) { // Init for data analzer server. XSCollector.shared = [[XSCollector alloc] initWithAppId:self.configuration.tdAppId gameId:self.configuration.tdGameId]; } } } [self sdk_checkVersion:^{ [self sdk_checkNote:^{ // Check workflow ended. [XSTracker.shared trace:0x010001]; // Init sdk complete. if (complete != nil) complete(nil); // init workflow ended. [XSTracker.shared trace:0x010002]; }]; }]; }]; } /// /// Login the sdk. /// \param complete This closure called when login complete. /// - (void)loginWithComplete:(void (^)(id user, NSError* error))complete { // If init is complete, ignore it this call. if (self.configuration == nil) { if (complete != nil) complete(nil, [XSNetwork errorWithCode:-1 message:@"请先初始化"]); // error -1 return; } [XSTracker.shared trace:0x010010]; // Unified operation after successful login. void (^loginedCallback)(void) = ^{ // Build return data. id user = nil; if (self.user != nil) { user = @{ @"uid": self.user.uid, @"token": self.user.token, @"userName": self.user.userName ?: @"", @"phone": self.user.phone ?: @"" }; // According to air bubbles. if (!self.configuration.isFloatDisable) { UIWindow* window = UIApplication.sharedApplication.delegate.window ?: UIApplication.sharedApplication.keyWindow; BubbleView* view = [[BubbleView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)]; [view.contentView addTarget:self action:@selector(center) forControlEvents:UIControlEventTouchUpInside]; [window addSubview:view]; // It's shown in the first one. UIView* xmp = [window viewWithTag:-1]; if (xmp != nil) { [window bringSubviewToFront:xmp]; } [self.bubbleView removeFromSuperview]; self.bubbleView = view; } [XSTracker.shared trace:0x010013]; } [XSTracker.shared trace:0x010011]; if (complete) { complete(user, nil); } [XSTracker.shared trace:0x010012]; }; // Try automatically login if needed. if (self.user != nil) { [XSNetwork showHudLoading]; [XSNetwork authWithUser:self.user.uid complete:^(XSUser* user, NSError *error) { // If the login fails, clear the user information and login again. if (user.token.length == 0 || error != nil) { [XSNetwork showHudFailure:@"登录时效期已过,请重新登录"]; self.user = nil; [self loginWithComplete:complete]; return; } // Update user info. XSUser* duser = self.user; duser.token = user.token; duser.isRegister = false; self.user = duser; // Login successful, display HUD. [XSNetwork showPrompt:duser.name]; // Check if the user is already connected to the phone. [self sdk_checkPhone:^{ loginedCallback(); }]; }]; [XSTracker.shared trace:0x010014]; return; } // Setup login callback. __weak __typeof(self) ws = self; self.sdk_loginCallback = ^(XSUser* user) { [ws setUser:user]; [ws.rootViewController dismissViewControllerAnimated:YES completion:nil]; // Login successful, display HUD. [XSNetwork showPrompt:user.name]; // Check if the user is already connected to the phone. [ws sdk_checkPhone:^{ loginedCallback(); }]; }; // The last time was to log out, so the quick login list is displayed. if (XSUser.history.count != 0) { self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginFastViewController.new]; return; } // To account login self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginPhoneViewController.new]; } /// /// Logout the sdk. /// \param complete This closure called when logout complete. /// - (void)logoutWithComplete:(void (^)(void))complete { // If init is complete, ignore it this call. if (self.configuration == nil) { if (complete != nil) complete(); return; } self.user = nil; [self.bubbleView removeFromSuperview]; self.bubbleView = nil; if (complete != nil) complete(); [XSTracker.shared trace:0x010020]; } /// /// Logout the game and sdk. /// \param handler This closure called when user logout in web view. /// \note Warning this handler will retain in until next call this method. /// - (void)logoutWhenNotification:(void (^)(void))handler { self.sdk_logoutCallback = ^(id user) { if (handler != nil) handler(); [XSTracker.shared trace:0x010020]; }; } /// /// Buy a product. /// \param parameters Payment request parameters. /// \param complete Payment result of the callback, if the apple payment is completed before the callback, other payments will be the official launch of the callback /// - (void)buy:(NSDictionary*)parameters complete:(void (^)(id order, NSError* error))complete { // Check paramrters. if (self.configuration == nil || self.user == nil) { if (complete != nil) { complete(nil, [XSNetwork errorWithCode:-1 message:@"请先登录"]); // error -1 return ; } } [XSTracker.shared trace:0x010030]; [XSNetwork showHudLoading]; [XSNetwork routeWithComplete:^(id object, NSError *error) { if (error != nil) { [XSNetwork showHudFailure:error]; if (complete != nil) { complete(nil, error); } return; } // Get parmeters. XSUser* user = self.user; NSString* path = object[fqsd(@"PREAACs/NCYsJjQuLRkA")]; #if SDK_HAS_H5_PAYMENT // Generate h5 payment parameters. if (path.length != 0) { [XSNetwork hideHud]; [XSTracker.shared trace:0x010031]; id payments = @{ @"cpBillNo": parameters[@"orderId"] ?: @"", @"orderAmount": parameters[@"amount"] ?: @"", @"subject": parameters[@"productName"] ?: @"", @"roleName": parameters[@"roleName"] ?: @"", @"serverId": parameters[@"serverId"] ?: @"", @"extraInfo": parameters[@"extraInfo"] ?: @"", @"phone": @"", @"isTest": @"1", @"orderPlatform": @"0", @"orderType": @"1", @"remark": @"0", @"isApp": @"0", @"uid": user.uid, @"userName": user.userName ?: @"" }; NSURLComponents* components = [NSURLComponents componentsWithString:path]; NSDictionary* json = [XSNetwork bodyWithParameters:payments]; NSString* str = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:json options:0 error:nil] encoding:NSUTF8StringEncoding]; components.queryItems = @[[NSURLQueryItem queryItemWithName:@"jsonData" value:str], [NSURLQueryItem queryItemWithName:@"v" value:@"1"]]; if (components.URL == nil) { if (complete != nil) { complete(nil, [XSNetwork errorWithCode:-1 message:@"后台参数错误"]); } return; } UserCenterViewController* rootViewController = UserCenterViewController.new; rootViewController.custom = components.URL.absoluteString; rootViewController.otherView = self.bubbleView; rootViewController.modalPresentationStyle = UIModalPresentationOverFullScreen; self.rootViewController = rootViewController; // Cannot get the callback state. rootViewController.callback = ^{ if (complete != nil) { complete(nil, nil); } }; return; } #endif #if SDK_HAS_IAP_PAYMENT // Generate iap payment parameters. id payments = @{ @"productCode": parameters[@"productCode"] ?: @"", @"isCoin": @"0", @"orderPlatform": @"0", @"orderType": @"1", @"orderAmount": parameters[@"amount"] ?: @"", @"isTest": @"0", @"subject": parameters[@"productName"] ?: @"", @"roleName": parameters[@"roleName"] ?: @"", @"cpBillNo": parameters[@"orderId"] ?: @"", @"remark": @"0", @"serverId": parameters[@"serverId"] ?: @"", @"extraInfo": parameters[@"extraInfo"] ?: @"", @"uid": user.uid, @"userName": user.userName ?: @"" }; [XSTracker.shared trace:0x010032]; [XSNetwork payWithParameters:payments complete:^(id object, NSError *error) { if (error != nil) { [XSNetwork showHudFailure:error]; if (complete != nil) { complete(nil, error); } return; } [XSTracker.shared trace:0x010034]; [XSNetwork showHudSuccess:@"支付成功"]; if (complete != nil) complete(payments, nil); }]; #endif }]; } /// /// User Center. /// - (void)center { if (self.configuration == nil || self.user == nil) { return; } [XSTracker.shared trace:0x010040]; UserCenterViewController* rootViewController = UserCenterViewController.new; rootViewController.otherView = self.bubbleView; rootViewController.modalPresentationStyle = UIModalPresentationOverFullScreen; self.rootViewController = rootViewController; } /// Report role info. /// \param roleName This role name. /// /// \param level This role current level. /// /// \param serverId This role current server. /// - (void)reportWithRoleName:(NSString*)roleName level:(NSInteger)level serverId:(NSString*)serverId { if (self.configuration == nil || self.user == nil) { return; } [XSNetwork reportWithUser:self.user.userName Name:roleName level:level server:serverId]; [XSCollector.shared reportWithAccount:self.user.uid name:roleName level:level server:serverId]; } // MARK: - - (void)sdk_checkVersion:(void(^)(void))complete { // If no configuration file is provided, initialization failed. if (self.configuration == nil) { complete(); return; } // If update cheker is not enabled, ignore it. NSURL* url = [NSURL URLWithString:self.configuration.updateUrl]; if (url == nil || self.configuration.updateState == 0) { complete(); return; } // If the prompt is too frequent, wait. // 1 is normal update. if (self.configuration.updateState == 1 && !([XSDataCenter doubleForKey:@"verion-later"] < NSDate.new.timeIntervalSince1970)) { complete(); return; } NoticeViewController* viewController = NoticeViewController.new; // Set the update title & contents. viewController.title = @"发现新版本"; viewController.contents = [NSString stringWithFormat:@"亲爱的玩家您好,我们发布了新的版本, 更新到新版本可体验更多新奇有趣的玩法!\n\n最新版本: %@\n当前版本: %@\n\n\n", self.configuration.updateVersion, self.shortVersion]; // Set the confirm closure. [viewController addActionWithName:@"立即升级" handler:^{ // -[UIApplication openURL:] ((void(*)(id,SEL,id))objc_msgSend)(UIApplication.sharedApplication, NSSelectorFromString(fqsd(@"p2cAAMrAyrKwq5rIAA==")), url); }]; // Set the cancel closure. if (self.configuration.updateState == 1) { __weak __typeof(self) ws = self; [viewController addActionWithName:@"稍后再说" handler:^{ // Again 24 hours later. [XSDataCenter setDouble:[NSDate dateWithTimeIntervalSinceNow:24*60*60].timeIntervalSince1970 forKey:@"verion-later"]; if (ws.rootViewController == nil) { complete(); return; } // Automatic hidden controller. [ws.rootViewController dismissViewControllerAnimated:YES completion:complete]; }]; } // Show view contorller in key window. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self.rootViewController = [[RootViewController alloc] initWithRootViewController:viewController]; }); } - (void)sdk_checkNote:(void(^)(void))complete { // If no configuration file is provided, initialization failed. NSURL* url = [NSURL URLWithString:self.configuration.closeUrl]; if (url == nil || self.configuration.linkQq == 0) { complete(); return ; } // If the announcement is displayed, it is no longer displayed. if (self.configuration.linkQq != 2 && [[XSDataCenter stringForKey:@"notice-later"] isEqualToString:url.absoluteString]) { complete(); return; } NoticeViewController* viewController = NoticeViewController.new; // Set the update title & contents. viewController.title = @"系统公告"; viewController.contents = url; // Set the confirm closure. __weak __typeof(self) ws = self; [viewController addActionWithName:@"我知道了" handler:^{ // Never again. [XSDataCenter setString:url.absoluteString forKey:@"notice-later"]; if (ws.rootViewController == nil) { complete(); return; } // Automatic hidden controller. [ws.rootViewController dismissViewControllerAnimated:YES completion:complete]; }]; // Show view contorller in key window. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self.rootViewController = [[RootViewController alloc] initWithRootViewController:viewController]; }); } - (void)sdk_checkPhone:(void(^)(void))complete { // Check if the phone character exists. if (self.user.isRegister || self.user.phone.length != 0 || self.configuration.serverStatus == 1) { complete(); return; } // If the prompt is too frequent, wait. id key = [NSString stringWithFormat:@"%@-bind-later", self.user.uid]; if ([XSDataCenter doubleForKey:key] > NSDate.new.timeIntervalSince1970) { complete(); return; } UserBindPhoneViewController* bindViewController = UserBindPhoneViewController.new; RootViewController* rooViewController = [[RootViewController alloc] initWithRootViewController:bindViewController]; UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"您的帐号存在风险, 为了您的帐号安全建议您绑定手机号。" preferredStyle:UIAlertControllerStyleAlert]; // Add confirm action. [alert addAction:[UIAlertAction actionWithTitle:@"绑定手机" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { bindViewController.callback = complete; self.rootViewController = rooViewController; }]]; // Add cancel action. [alert addAction:[UIAlertAction actionWithTitle:@"稍后提醒" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // Again 24 hours later. [XSDataCenter setDouble:[NSDate dateWithTimeIntervalSinceNow:24*60*60].timeIntervalSince1970 forKey:key]; // Continue. complete(); }]]; (void)rooViewController.view; // preload; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ self.rootViewController = alert; }); // Hide all hud if needed. [XSNetwork hideHud]; } // MARK: - - (void)setUser:(XSUser *)user { _user = user; _token = user.token; // Cache. [XSDataCenter setValue:user forKey:@"User.current"]; if (user == nil) { return; } // Make a new login history. NSMutableSet* ids = [NSMutableSet set]; NSMutableArray* history = [NSMutableArray array]; user.lastTime = NSDate.new.timeIntervalSince1970; [ids addObject:user.uid]; [history addObject:user]; for (XSUser* tmp in XSUser.history) { if (![ids containsObject:tmp.uid]) { [ids addObject:tmp.uid]; [history addObject:tmp]; } } XSUser.history = history; } - (XSUser*)user { if (_user != nil) { return _user; } static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ self->_user = [XSDataCenter valueForKey:@"User.current"]; self->_token = self->_user.token; }); return _user; } - (void)setRootViewController:(UIViewController *)rootViewController { _rootViewController = rootViewController; if (rootViewController == nil) { return; } [UIApplication.sharedApplication.keyWindow.rootViewController presentViewController:rootViewController animated:YES completion:nil]; } /// Permanent preservation of data. - (void)setPermanentValue:(NSString*)value forKey:(NSString*)key { // Set value to user defautls. [XSDataCenter setString:value forKey:key]; // Configure the search setting. id setting = @{ // Configure the keychain key type. CFBridgingRelease(kSecClass): CFBridgingRelease(kSecClassGenericPassword), CFBridgingRelease(kSecAttrAccount): key }.mutableCopy; // Delete old item before add new item SecItemDelete(CFBridgingRetain(setting)); // Add new object to search dictionary. [setting setObject:[value dataUsingEncoding:NSUTF8StringEncoding] forKey:CFBridgingRelease(kSecValueData)]; // Add item to keychain with the search dictionary SecItemAdd(CFBridgingRetain(setting), nil); } /// Read data for permanent preservationed. - (id)permanentValueForKey:(NSString*)key { // Read data from user defatuls. NSString* udid = [XSDataCenter stringForKey:key]; if (udid.length != 0) { return udid; } // Configure the search setting. id setting = @{ // Configure the keychain key type. CFBridgingRelease(kSecClass): CFBridgingRelease(kSecClassGenericPassword), // The class name of the SDK is dynamic. CFBridgingRelease(kSecAttrService): NSStringFromClass(self.class), CFBridgingRelease(kSecAttrAccount): key, // Gets result data of data. CFBridgingRelease(kSecReturnData): CFBridgingRelease(kCFBooleanTrue) }; // Start query the keychain query. CFTypeRef result = nil; if (SecItemCopyMatching(CFBridgingRetain(setting), &result) == noErr) { return nil; } // Convert data to a string NSString* value = [[NSString alloc] initWithData:CFBridgingRelease(result) encoding:NSUTF8StringEncoding]; if (value.length == 0) { return nil; } // Set value to user defautls, reduce Keychain access count. [XSDataCenter setString:value forKey:key]; return value; } - (NSString*)advertisingIdentifier { // Read the data from keychain. NSString* advertisingIdentifier = [self permanentValueForKey:@"udid"]; if (advertisingIdentifier.length != 0) { return advertisingIdentifier; } #if SDK_HAS_IDFA // Try to obtain idfa. ASIdentifierManager* advertisingManager = ASIdentifierManager.sharedManager; advertisingIdentifier = advertisingManager.advertisingIdentifier.UUIDString; if (!advertisingManager.isAdvertisingTrackingEnabled) { advertisingIdentifier = [NSString stringWithFormat:@"#%@", NSUUID.new.UUIDString]; } #else // Generate an id to replace idfa. advertisingIdentifier = [NSString stringWithFormat:@"#%@", NSUUID.new.UUIDString]; #endif // Save to keychain to prevent updates. [self setPermanentValue:advertisingIdentifier forKey:@"udid"]; return advertisingIdentifier; } - (NSString*)advertisingVendor { // This parameter has been deprecated and must be empty string. return @""; } - (NSBundle*)bundle { return [NSBundle bundleForClass:self.class]; } - (NSString*)model { struct utsname sinfo; uname(&sinfo); return [NSString stringWithUTF8String: sinfo.machine]; } /// The sdk verions. - (NSString*)version { return self.bundle.infoDictionary[@"CFBundleShortVersionString"] ?: @"1.0"; } /// The game verions. - (NSString*)shortVersion { return NSBundle.mainBundle.infoDictionary[@"CFBundleShortVersionString"] ?: @"1.0"; } //横幅广告 -(void)adBanner:(NSString *)adId { [[FqGameAd fqGameAD] showBanner:adId]; } //关闭横幅广告 - (void)closeBanner:(NSString *)adId { [[FqGameAd fqGameAD] closeBanner:adId]; } //显示插页广告 -(void)adInner:(NSString *)adId complete:(void (^)(int))complete { [[FqGameAd fqGameAD] showInner:adId callback:^(int intValue) { complete(intValue); }]; } //视频激励广告 -(void)adVideo:(NSString *)adId complete:(void (^)(int))complete { [[FqGameAd fqGameAD] showVideo:adId callback:^(int intValue) { complete(intValue); }]; } //广告是否准备好 /// @parm callback YES:可用, NO:不可用 -(void)isAdReady:(NSString *)adName callback:(void(^)(BOOL))callback { BOOL ready = [[FqGameAd fqGameAD] isAdReady:adName]; callback(ready); } //打开广告 -(void)openAd:(NSString *)adName { [[FqGameAd fqGameAD] openAd:adName]; } //打开原生广告 -(void)adNative:(NSString *)adId x:(int)x y:(int)y w:(int)width h:(int)hight { [[FqGameAd fqGameAD] showAdNative:adId x:x y:y w:width h:hight]; } //关闭原生广告 -(void)closeNative { [[FqGameAd fqGameAD] closeNative]; } //关闭原生广告(通过adId) -(void)closeNative:(NSString *)adName { [[FqGameAd fqGameAD] closeNative:adName]; } ///生命周期启动入口 /// @param application application对象 -(void)applicationDidFinishLaunching:(UIApplication *)application { //... } /// 游戏进入后台 -(void)applicationDidEnterBackground{ //... } /// 游戏进入到前台 -(void)applicationWillEnterForeground{ //... } /// 游戏开始活跃运行 -(void)applicationDidBecomeActive{ //... } ///手机震动 -(void)iphoneVibrate { //手机--设置--声音--响铃模式震动(打开),不然无效. AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); } //跳转appstore评论 -(void)skipToAppsotreWithID:(NSString *)appleId { NSString *urlStr = [NSString stringWithFormat:@"https://itunes.apple.com/us/app/itunes-u/id%@?action=write-review&mt=8", appleId]; [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]]; } ///自动登录 -(void)autoLogin { XSUser* user = [XSDataCenter valueForKey:@"User.current"]; if (user.uid.length == 0) { return; } // Try authorization login. XenonSDK.sharedSDK.token = user.token; [XSNetwork authWithUser:user.uid complete:^(XSUser* object, NSError *error) { // Check for general errors. if (error != nil && ![error.domain isEqual:@"XSNetwork"]) { [XSNetwork showHudFailure:error]; return; } // Login successful. if (user.token.length != 0 && error == nil) { // Update user info. user.token = object.token; user.isRegister = NO; XenonSDK.sharedSDK.user = user; if (XenonSDK.sharedSDK.sdk_loginCallback) { XenonSDK.sharedSDK.sdk_loginCallback(user); } return; } [XSNetwork showHudFailure:@"登录时效期已过,请重新登录"]; // Login failed to login page. UserLoginPhoneViewController* viewController = UserLoginPhoneViewController.new; [viewController automatic:user.account]; [self.rootViewController.navigationController pushViewController:viewController animated:YES]; }]; } ///手动登录 -(void)manualLogin { //XSUser *user = [XSDataCenter valueForKey:@"User.current"]; if (XSUser.history.count != 0) { self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginFastViewController.new]; return; } // To account login self.rootViewController = [[RootViewController alloc] initWithRootViewController:UserLoginPhoneViewController.new]; } @end