diff --git a/CustomKeyboard/Network/KBNetworkManager.h b/CustomKeyboard/Network/KBNetworkManager.h index c0dfc9e..b319dac 100644 --- a/CustomKeyboard/Network/KBNetworkManager.h +++ b/CustomKeyboard/Network/KBNetworkManager.h @@ -19,8 +19,15 @@ typedef NS_ERROR_ENUM(KBNetworkErrorDomain, KBNetworkError) { KBNetworkErrorDecodeFailed = 4, }; -/// 简单的 JSON 回调:json 为 NSDictionary/NSArray 或者在非 JSON 情况下返回 NSData -typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error); +/// JSON 回调(扩展侧目前很少使用 JSON,可按需扩展) +typedef void(^KBNetworkCompletion)(NSDictionary *_Nullable json, + NSURLResponse * _Nullable response, + NSError * _Nullable error); + +/// 二进制回调:用于下载 zip、图片等原始数据 +typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error); @interface KBNetworkManager : NSObject @@ -45,6 +52,12 @@ typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nul headers:(nullable NSDictionary *)headers completion:(KBNetworkCompletion)completion; +/// GET 原始二进制数据(不做 JSON 解析) +- (nullable NSURLSessionDataTask *)GETData:(NSString *)path + parameters:(nullable NSDictionary *)parameters + headers:(nullable NSDictionary *)headers + completion:(KBNetworkDataCompletion)completion; + /// POST JSON 请求,jsonBody 会以 application/json 发送 - (nullable NSURLSessionDataTask *)POST:(NSString *)path jsonBody:(nullable id)jsonBody @@ -54,4 +67,3 @@ typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nul @end NS_ASSUME_NONNULL_END - diff --git a/CustomKeyboard/Network/KBNetworkManager.m b/CustomKeyboard/Network/KBNetworkManager.m index 0c2646b..63dcb41 100644 --- a/CustomKeyboard/Network/KBNetworkManager.m +++ b/CustomKeyboard/Network/KBNetworkManager.m @@ -60,7 +60,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; return nil; } [self applyHeaders:headers toMutableRequest:req contentType:nil]; - return [self startAFTaskWithRequest:req completion:completion]; + return [self startAFJSONTaskWithRequest:req completion:completion]; } - (NSURLSessionDataTask *)POST:(NSString *)path @@ -80,7 +80,41 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; error:&error]; if (error) { if (completion) completion(nil, nil, error); return nil; } [self applyHeaders:headers toMutableRequest:req contentType:nil]; - return [self startAFTaskWithRequest:req completion:completion]; + return [self startAFJSONTaskWithRequest:req completion:completion]; +} + +- (NSURLSessionDataTask *)GETData:(NSString *)path + parameters:(NSDictionary *)parameters + headers:(NSDictionary *)headers + completion:(KBNetworkDataCompletion)completion { + if (!self.isEnabled) { + NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain + code:KBNetworkErrorDisabled + userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Network disabled (Full Access may be off)")}]; + if (completion) completion(nil, nil, e); + return nil; + } + NSString *urlString = [self buildURLStringWithPath:path]; + if (!urlString) { + NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain + code:KBNetworkErrorInvalidURL + userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid URL")}]; + if (completion) completion(nil, nil, e); + return nil; + } + AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer]; + serializer.timeoutInterval = self.timeout; + NSError *serror = nil; + NSMutableURLRequest *req = [serializer requestWithMethod:@"GET" + URLString:urlString + parameters:parameters + error:&serror]; + if (serror || !req) { + if (completion) completion(nil, nil, serror ?: [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid URL")}]); + return nil; + } + [self applyHeaders:headers toMutableRequest:req contentType:nil]; + return [self startAFDataTaskWithRequest:req completion:completion]; } #pragma mark - Core @@ -120,7 +154,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; [all enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) { [req setValue:obj forHTTPHeaderField:key]; }]; } -- (NSURLSessionDataTask *)startAFTaskWithRequest:(NSURLRequest *)req completion:(KBNetworkCompletion)completion { +- (NSURLSessionDataTask *)startAFJSONTaskWithRequest:(NSURLRequest *)req completion:(KBNetworkCompletion)completion { // 响应先用原始数据返回,再按 Content-Type 解析 JSON(与原实现一致) self.manager.responseSerializer = [AFHTTPResponseSerializer serializer]; NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { @@ -154,16 +188,41 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; if (looksJSON) { NSError *jsonErr = nil; id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr]; - if (jsonErr) { if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Failed to parse JSON")}]); return; } - if (completion) completion(json, response, nil); + if (jsonErr) { + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Failed to parse JSON")}]); + return; + } + if (![json isKindOfClass:[NSDictionary class]]) { + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]); + return; + } + if (completion) completion((NSDictionary *)json, response, nil); } else { - if (completion) completion(data, response, nil); + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]); } }]; [task resume]; return task; } +- (NSURLSessionDataTask *)startAFDataTaskWithRequest:(NSURLRequest *)req completion:(KBNetworkDataCompletion)completion { + self.manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + if (error) { + if (completion) completion(nil, response, error); + return; + } + NSData *data = (NSData *)responseObject; + if (![data isKindOfClass:[NSData class]]) { + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"No data")}]); + return; + } + if (completion) completion(data, response, nil); + }]; + [task resume]; + return task; +} + #pragma mark - AFHTTPSessionManager - (AFHTTPSessionManager *)manager { diff --git a/Shared/KBSkinInstallBridge.m b/Shared/KBSkinInstallBridge.m index 044c8b1..30a44d4 100644 --- a/Shared/KBSkinInstallBridge.m +++ b/Shared/KBSkinInstallBridge.m @@ -210,10 +210,8 @@ static NSString * const kKBSkinPendingIconShortKey = @"iconShortNames"; // 远程下载(http/https) NSLog(@"[SkinBridge] will GET zip: %@", zipURL); [KBHUD show]; - [[KBNetworkManager shared] GET:zipURL parameters:nil headers:nil completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { + [[KBNetworkManager shared] GETData:zipURL parameters:nil headers:nil completion:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"[SkinBridge] GET finished, error = %@", error); -// [KBHUD dismiss]; - NSData *data = ([jsonOrData isKindOfClass:NSData.class] ? (NSData *)jsonOrData : nil); if (error || data.length == 0) { zipOK = NO; if (!innerError) { diff --git a/keyBoard/Class/Guard/VC/KBSexSelVC.m b/keyBoard/Class/Guard/VC/KBSexSelVC.m index c9edbd4..d9e3811 100644 --- a/keyBoard/Class/Guard/VC/KBSexSelVC.m +++ b/keyBoard/Class/Guard/VC/KBSexSelVC.m @@ -70,7 +70,7 @@ typedef NS_ENUM(NSInteger, KBSexOption) { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [[KBNetworkManager shared] GET:@"https://www.apple.com.cn/" parameters:nil headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + [[KBNetworkManager shared] GET:@"https://www.apple.com.cn/" parameters:nil headers:nil completion:^(NSDictionary * _Nullable json, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"===="); }]; }); diff --git a/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m b/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m index 7fa1fcf..6c85437 100644 --- a/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m +++ b/keyBoard/Class/Home/VC/FunctionTest/KBSkinCenterVC.m @@ -110,7 +110,7 @@ [[KBNetworkManager shared] GET:@"https://www.apple.com" parameters:nil headers:nil - completion:^(id jsonOrData, NSURLResponse *response, NSError *error) { + completion:^(NSDictionary *json, NSURLResponse *response, NSError *error) { NSLog(@"[Test] apple.com finished, error = %@", error); }]; } diff --git a/keyBoard/Class/Home/VC/FunctionTest/KBTestVC.m b/keyBoard/Class/Home/VC/FunctionTest/KBTestVC.m index abf7b06..3158db0 100644 --- a/keyBoard/Class/Home/VC/FunctionTest/KBTestVC.m +++ b/keyBoard/Class/Home/VC/FunctionTest/KBTestVC.m @@ -44,7 +44,7 @@ dispatch_async(dispatch_get_main_queue(), ^{ [self.textView becomeFirstResponder]; }); - [[KBNetworkManager shared] GET:KB_API_APP_CONFIG parameters:nil headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + [[KBNetworkManager shared] GET:KB_API_APP_CONFIG parameters:nil headers:nil completion:^(NSDictionary * _Nullable json, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSLog(@"===="); }]; } diff --git a/keyBoard/Class/Login/VM/KBLoginVM.m b/keyBoard/Class/Login/VM/KBLoginVM.m index 2279d77..2d0ec2b 100644 --- a/keyBoard/Class/Login/VM/KBLoginVM.m +++ b/keyBoard/Class/Login/VM/KBLoginVM.m @@ -45,7 +45,7 @@ if (cred.user.length) params[@"userID"] = cred.user; // 可选 [KBHUD show]; // 向服务端发起校验 - [[KBNetworkManager shared] POST:API_APPLE_LOGIN jsonBody:params headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + [[KBNetworkManager shared] POST:API_APPLE_LOGIN jsonBody:params headers:nil completion:^(NSDictionary * _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { [KBHUD dismiss]; if (error) { if (completion) completion(NO, error); return; } diff --git a/keyBoard/Class/Me/VM/KBMyVM.m b/keyBoard/Class/Me/VM/KBMyVM.m index 02ffc96..effec0f 100644 --- a/keyBoard/Class/Me/VM/KBMyVM.m +++ b/keyBoard/Class/Me/VM/KBMyVM.m @@ -11,23 +11,34 @@ @implementation KBMyVM - (void)logout{ [KBHUD show]; - [[KBNetworkManager shared] GET:API_LOGOUT parameters:nil headers:nil completion:^(NSDictionary * jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { - if (error != nil) { + [[KBNetworkManager shared] GET:API_LOGOUT + parameters:nil + headers:nil + completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + // 不管成功失败,都先把 HUD 收掉 + [KBHUD dismiss]; + + if (error) { + // 如果是业务错误,可以把服务端 message 提示出来 + NSString *msg = KBBizMessageFromJSONObject(jsonOrData) ?: error.localizedDescription ?: KBLocalized(@"Network error"); + [KBHUD showInfo:msg]; return; } - NSString *message = jsonOrData[KBMessage]; -// if (code == KBBizCodeSuccess) { + + NSString *message = jsonOrData[KBMessage] ?: KBLocalized(@"Success"); [KBHUD showSuccess:message]; - [[KBUserSessionManager shared] logout]; - // 登录成功后切换到主 TabBar - dispatch_async(dispatch_get_main_queue(), ^{ - id appDelegate = UIApplication.sharedApplication.delegate; - if ([appDelegate respondsToSelector:@selector(setupRootVC)]) { - AppDelegate *delegate = (AppDelegate *)appDelegate; - [delegate setupRootVC]; - } - }); -// } + + // 本地会话退出 + [[KBUserSessionManager shared] logout]; + + // 回到登录 / 主界面 + dispatch_async(dispatch_get_main_queue(), ^{ + id appDelegate = UIApplication.sharedApplication.delegate; + if ([appDelegate respondsToSelector:@selector(setupRootVC)]) { + AppDelegate *delegate = (AppDelegate *)appDelegate; + [delegate setupRootVC]; + } + }); }]; } @end diff --git a/keyBoard/Class/Network/KBNetworkManager.h b/keyBoard/Class/Network/KBNetworkManager.h index 8ac946d..bd3d42b 100644 --- a/keyBoard/Class/Network/KBNetworkManager.h +++ b/keyBoard/Class/Network/KBNetworkManager.h @@ -20,8 +20,15 @@ typedef NS_ERROR_ENUM(KBNetworkErrorDomain, KBNetworkError) { KBNetworkErrorBusiness = 5, // 服务端返回非成功业务 code }; -/// 简单的 JSON 回调:json 为 NSDictionary/NSArray 或者在非 JSON 情况下返回 NSData -typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error); +/// JSON 回调:约定服务端统一返回顶层 NSDictionary({code,message,data,...}) +typedef void(^KBNetworkCompletion)(NSDictionary *_Nullable json, + NSURLResponse * _Nullable response, + NSError * _Nullable error); + +/// 二进制回调:用于下载 zip、图片等原始数据 +typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data, + NSURLResponse *_Nullable response, + NSError *_Nullable error); @interface KBNetworkManager : NSObject @@ -46,6 +53,12 @@ typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nul headers:(nullable NSDictionary *)headers completion:(KBNetworkCompletion)completion; +/// GET 原始二进制数据(不做 JSON 解析/业务 code 处理) +- (nullable NSURLSessionDataTask *)GETData:(NSString *)path + parameters:(nullable NSDictionary *)parameters + headers:(nullable NSDictionary *)headers + completion:(KBNetworkDataCompletion)completion; + /// POST JSON 请求,jsonBody 会以 application/json 发送 - (nullable NSURLSessionDataTask *)POST:(NSString *)path jsonBody:(nullable id)jsonBody @@ -55,4 +68,3 @@ typedef void(^KBNetworkCompletion)(id _Nullable jsonOrData, NSURLResponse * _Nul @end NS_ASSUME_NONNULL_END - diff --git a/keyBoard/Class/Network/KBNetworkManager.m b/keyBoard/Class/Network/KBNetworkManager.m index a908557..35a7452 100644 --- a/keyBoard/Class/Network/KBNetworkManager.m +++ b/keyBoard/Class/Network/KBNetworkManager.m @@ -84,7 +84,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; req.allHTTPHeaderFields ?: @{}, paramStr); #endif - return [self startAFTaskWithRequest:req completion:completion]; + return [self startJSONTaskWithRequest:req completion:completion]; } - (NSURLSessionDataTask *)POST:(NSString *)path @@ -113,7 +113,42 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; req.allHTTPHeaderFields ?: @{}, bodyStr); #endif - return [self startAFTaskWithRequest:req completion:completion]; + return [self startJSONTaskWithRequest:req completion:completion]; +} + +// 原始二进制 GET,用于下载 zip、图片等 +- (NSURLSessionDataTask *)GETData:(NSString *)path + parameters:(NSDictionary *)parameters + headers:(NSDictionary *)headers + completion:(KBNetworkDataCompletion)completion { + if (!self.isEnabled) { + NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain + code:KBNetworkErrorDisabled + userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Network disabled (Full Access may be off)")}]; + if (completion) completion(nil, nil, e); + return nil; + } + NSString *urlString = [self buildURLStringWithPath:path]; + if (!urlString) { + NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain + code:KBNetworkErrorInvalidURL + userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid URL")}]; + if (completion) completion(nil, nil, e); + return nil; + } + AFHTTPRequestSerializer *serializer = [AFHTTPRequestSerializer serializer]; + serializer.timeoutInterval = self.timeout; + NSError *serror = nil; + NSMutableURLRequest *req = [serializer requestWithMethod:@"GET" + URLString:urlString + parameters:parameters + error:&serror]; + if (serror || !req) { + if (completion) completion(nil, nil, serror ?: [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Invalid URL")}]); + return nil; + } + [self applyHeaders:headers toMutableRequest:req contentType:nil]; + return [self startDataTaskWithRequest:req completion:completion]; } #pragma mark - Core @@ -154,7 +189,8 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; [all enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) { [req setValue:obj forHTTPHeaderField:key]; }]; } -- (NSURLSessionDataTask *)startAFTaskWithRequest:(NSURLRequest *)req completion:(KBNetworkCompletion)completion { +- (NSURLSessionDataTask *)startJSONTaskWithRequest:(NSURLRequest *)req + completion:(KBNetworkCompletion)completion { NSLog(@"[KBNetworkManager] startAFTaskWithRequest: %@", req.URL.absoluteString); // 响应先用原始数据返回,再按 Content-Type 解析 JSON(与原实现一致) self.manager.responseSerializer = [AFHTTPResponseSerializer serializer]; @@ -178,9 +214,9 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; #if DEBUG KBLOG(@"无效响应\nURL: %@\n说明: %@", req.URL.absoluteString, KBLocalized(@"未获取到数据")); #endif - if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"No data")}]); - return; - } + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"No data")}]); + return; + } NSString *ct = nil; if ([response isKindOfClass:[NSHTTPURLResponse class]]) { ct = ((NSHTTPURLResponse *)response).allHeaderFields[@"Content-Type"]; @@ -210,6 +246,12 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorDecodeFailed userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Failed to parse JSON")}]); return; } + + // 必须为字典结构,否则视为无效响应 + if (![json isKindOfClass:[NSDictionary class]]) { + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]); + return; + } #if DEBUG NSString *pretty = [self kb_prettyJSONStringFromObject:json] ?: @"(null)"; pretty = [self kb_trimmedString:pretty maxLength:4096]; @@ -220,12 +262,12 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; ct ?: @"", pretty); #endif + NSDictionary *dict = (NSDictionary *)json; // 统一解析业务 code:约定后端顶层包含 { code, message, data } - if ([json isKindOfClass:NSDictionary.class]) { - NSInteger bizCode = KBBizCodeFromJSONObject(json); - if (bizCode != NSNotFound && bizCode != KBBizCodeSuccess) { + NSInteger bizCode = KBBizCodeFromJSONObject(dict); + if (bizCode != NSNotFound && bizCode != KBBizCodeSuccess) { // 非成功业务 code:执行通用处理(如 token 失效)并通过 error 方式回调 - [self kb_handleBizCode:bizCode json:json response:response]; + [self kb_handleBizCode:bizCode json:dict response:response]; NSString *msg = KBBizMessageFromJSONObject(json) ?: KBLocalized(@"Server error"); NSError *bizErr = [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorBusiness @@ -233,30 +275,42 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network"; NSLocalizedDescriptionKey : msg, @"code" : @(bizCode) }]; - if (completion) completion(json, response, bizErr); + if (completion) completion(dict, response, bizErr); return; } - } // code 缺失或为成功,按正常成功回调 - if (completion) completion(json, response, nil); + if (completion) completion(dict, response, nil); } else { -#if DEBUG - NSString *text = [self kb_textFromData:data]; - text = [self kb_trimmedString:text maxLength:4096]; - NSInteger status = [response isKindOfClass:NSHTTPURLResponse.class] ? ((NSHTTPURLResponse *)response).statusCode : 0; - KBLOG(@"\n[KBNetwork] 响应成功(Data)\nURL: %@\n状态: %ld\nContent-Type: %@\n数据: %@\n", - req.URL.absoluteString, - (long)status, - ct ?: @"", - text); -#endif - if (completion) completion(data, response, nil); + // 预期 JSON,但未检测到 JSON 内容 + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"Invalid response")}]); } }]; [task resume]; return task; } +// 原始二进制任务 +- (NSURLSessionDataTask *)startDataTaskWithRequest:(NSURLRequest *)req + completion:(KBNetworkDataCompletion)completion { + NSLog(@"[KBNetworkManager] startDataTaskWithRequest: %@", req.URL.absoluteString); + self.manager.responseSerializer = [AFHTTPResponseSerializer serializer]; + NSURLSessionDataTask *task = [self.manager dataTaskWithRequest:req uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) { + NSLog(@"[KBNetworkManager] data task finished, error = %@", error); + if (error) { + if (completion) completion(nil, response, error); + return; + } + NSData *data = (NSData *)responseObject; + if (![data isKindOfClass:[NSData class]]) { + if (completion) completion(nil, response, [NSError errorWithDomain:KBNetworkErrorDomain code:KBNetworkErrorInvalidResponse userInfo:@{NSLocalizedDescriptionKey:KBLocalized(@"No data")}]); + return; + } + if (completion) completion(data, response, nil); + }]; + [task resume]; + return task; +} + #pragma mark - AFHTTPSessionManager - (AFHTTPSessionManager *)manager { diff --git a/keyBoard/Class/Pay/VM/PayVM.m b/keyBoard/Class/Pay/VM/PayVM.m index 5ff8fb9..717d68e 100644 --- a/keyBoard/Class/Pay/VM/PayVM.m +++ b/keyBoard/Class/Pay/VM/PayVM.m @@ -14,7 +14,7 @@ completion:(KBPayCompletion)completion { if (needShow) { [KBHUD show]; } - [[KBNetworkManager shared] POST:KB_API_IAP_VERIFY jsonBody:params headers:nil completion:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { + [[KBNetworkManager shared] POST:KB_API_IAP_VERIFY jsonBody:params headers:nil completion:^(NSDictionary * _Nullable json, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (needShow) { [KBHUD dismiss]; } if (error) {