This commit is contained in:
2025-12-03 14:30:02 +08:00
parent c1eb6a3458
commit 599a5de3bc
9 changed files with 180 additions and 66 deletions

View File

@@ -10,6 +10,6 @@
@interface AppDelegate : UIResponder <UIApplicationDelegate> @interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) UIWindow *window;
- (void)setupRootVC; - (void)toMainTabbarVC;
@end @end

View File

@@ -94,13 +94,20 @@
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
// token TabBar // token TabBar
BOOL loggedIn = [[KBUserSessionManager shared] isLoggedIn]; // BOOL loggedIn = [[KBUserSessionManager shared] isLoggedIn];
UIViewController *rootVC = nil; UIViewController *rootVC = nil;
if (loggedIn) {
rootVC = [[BaseTabBarController alloc] init]; rootVC = [[BaseTabBarController alloc] init];
} else {
rootVC = [[KBLoginVC alloc] init]; self.window.rootViewController = rootVC;
} }
- (void)toMainTabbarVC{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
UIViewController *rootVC = nil;
rootVC = [[BaseTabBarController alloc] init];
self.window.rootViewController = rootVC; self.window.rootViewController = rootVC;
} }
@@ -209,35 +216,37 @@
} }
- (void)goLogin{ - (void)goLogin{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ KBLoginVC *vc = [[KBLoginVC alloc] init];
KBLoginPopView *view = [[KBLoginPopView alloc] initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, KB_SCREEN_WIDTH)]; [KB_CURRENT_NAV pushViewController:vc animated:true];
// // dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
LSTPopView *pop = [LSTPopView initWithCustomView:view // KBLoginPopView *view = [[KBLoginPopView alloc] initWithFrame:CGRectMake(0, 0, KB_SCREEN_WIDTH, KB_SCREEN_WIDTH)];
parentView:nil // //
popStyle:LSTPopStyleSmoothFromBottom // LSTPopView *pop = [LSTPopView initWithCustomView:view
dismissStyle:LSTDismissStyleSmoothToBottom]; // parentView:nil
pop.hemStyle = LSTHemStyleBottom; // popStyle:LSTPopStyleSmoothFromBottom
pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4]; // dismissStyle:LSTDismissStyleSmoothToBottom];
pop.isClickBgDismiss = YES; // // pop.hemStyle = LSTHemStyleBottom;
pop.cornerRadius = 0; // view // pop.bgColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
// pop.isClickBgDismiss = YES; //
__weak typeof(pop) weakPop = pop; // pop.cornerRadius = 0; // view
view.appleLoginHandler = ^{ //
[weakPop dismiss]; // __weak typeof(pop) weakPop = pop;
// VM Apple + // view.appleLoginHandler = ^{
[[KBLoginVM shared] signInWithAppleFromViewController:KB_CURRENT_NAV completion:^(BOOL success, NSError * _Nullable error) { // [weakPop dismiss];
if (success) { // // VM Apple +
[KBHUD showInfo:KBLocalized(@"Signed in successfully")]; // [[KBLoginVM shared] signInWithAppleFromViewController:KB_CURRENT_NAV completion:^(BOOL success, NSError * _Nullable error) {
} else { // if (success) {
NSString *msg = error.localizedDescription ?: KBLocalized(@"Sign-in failed"); // [KBHUD showInfo:KBLocalized(@"Signed in successfully")];
[KBHUD showInfo:msg]; // } else {
} // NSString *msg = error.localizedDescription ?: KBLocalized(@"Sign-in failed");
}]; // [KBHUD showInfo:msg];
}; // }
view.closeHandler = ^{ [weakPop dismiss]; }; // }];
// };
[pop pop]; // view.closeHandler = ^{ [weakPop dismiss]; };
}); //
// [pop pop];
// });
} }

View File

@@ -164,6 +164,11 @@
#pragma mark - Actions #pragma mark - Actions
- (void)onTapBuyAction { - (void)onTapBuyAction {
// if (self.onTapBuy) { self.onTapBuy(); } // if (self.onTapBuy) { self.onTapBuy(); }
//
if (![KBUserSessionManager shared].isLoggedIn) {
[[KBUserSessionManager shared] goLoginVC];
return;
}
KBVipPay *vc = [[KBVipPay alloc] init]; KBVipPay *vc = [[KBVipPay alloc] init];
[KB_CURRENT_NAV pushViewController:vc animated:true]; [KB_CURRENT_NAV pushViewController:vc animated:true];
} }

View File

@@ -15,6 +15,7 @@
// //
@property (nonatomic, strong) UIImageView *bgImageView; // login_bg_icon @property (nonatomic, strong) UIImageView *bgImageView; // login_bg_icon
@property (nonatomic, strong) UIImageView *topRightImageView; // login_jianp_icon @property (nonatomic, strong) UIImageView *topRightImageView; // login_jianp_icon
@property (nonatomic, strong) UIButton *backButton; //
// 26 // 26
@property (nonatomic, strong) UIView *contentContainerView; @property (nonatomic, strong) UIView *contentContainerView;
@@ -75,6 +76,14 @@
make.edges.equalTo(self.view); make.edges.equalTo(self.view);
}]; }];
//
[self.view addSubview:self.backButton];
[self.backButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(16);
make.top.equalTo(self.view).offset(KB_StatusBarHeight() + 8);
make.width.height.mas_equalTo(32);
}];
// //
[self.view addSubview:self.topRightImageView]; [self.view addSubview:self.topRightImageView];
[self.topRightImageView mas_makeConstraints:^(MASConstraintMaker *make) { [self.topRightImageView mas_makeConstraints:^(MASConstraintMaker *make) {
@@ -165,7 +174,7 @@
id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate; id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
if ([appDelegate respondsToSelector:@selector(setupRootVC)]) { if ([appDelegate respondsToSelector:@selector(setupRootVC)]) {
AppDelegate *delegate = (AppDelegate *)appDelegate; AppDelegate *delegate = (AppDelegate *)appDelegate;
[delegate setupRootVC]; [delegate toMainTabbarVC];
} }
}); });
} else { } else {
@@ -195,6 +204,15 @@
KBLOG(@"onTapForgotPassword"); KBLOG(@"onTapForgotPassword");
} }
- (void)onTapBack {
// BaseViewController pop dismiss
if (self.navigationController && self.navigationController.viewControllers.count > 1) {
[self.navigationController popViewControllerAnimated:YES];
} else if (self.presentingViewController) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
#pragma mark - Lazy UI #pragma mark - Lazy UI
- (UIImageView *)bgImageView { - (UIImageView *)bgImageView {
@@ -223,6 +241,21 @@
return _contentContainerView; return _contentContainerView;
} }
- (UIButton *)backButton {
if (!_backButton) {
_backButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *img = [UIImage imageNamed:@"back"];
if (!img) { img = [UIImage imageNamed:@"back_black_icon"]; }
if (img) {
[_backButton setImage:img forState:UIControlStateNormal];
}
_backButton.adjustsImageWhenHighlighted = YES;
_backButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[_backButton addTarget:self action:@selector(onTapBack) forControlEvents:UIControlEventTouchUpInside];
}
return _backButton;
}
- (UILabel *)titleLabel { - (UILabel *)titleLabel {
if (!_titleLabel) { if (!_titleLabel) {
_titleLabel = [UILabel new]; _titleLabel = [UILabel new];

View File

@@ -36,6 +36,8 @@ NS_ASSUME_NONNULL_BEGIN
/// 退出登录:清 Keychain + 本地缓存 /// 退出登录:清 Keychain + 本地缓存
- (void)logout; - (void)logout;
- (void)goLoginVC;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -10,7 +10,7 @@
#import "KBUser.h" #import "KBUser.h"
#import "KBConfig.h" #import "KBConfig.h"
#import <MJExtension/MJExtension.h> #import <MJExtension/MJExtension.h>
#import "KBLoginVC.h"
/// App Group key /// App Group key
static NSString * const kKBSessionUserStoreKey = @"KBSession.currentUser"; static NSString * const kKBSessionUserStoreKey = @"KBSession.currentUser";
/// key /// key
@@ -58,14 +58,17 @@ static NSString * const kKBSessionInstallFlagKey = @"KBSession.installInitialize
if (self.didBootstrap) return; if (self.didBootstrap) return;
self.didBootstrap = YES; self.didBootstrap = YES;
BOOL hasInitializedThisInstall = [self.defaults boolForKey:kKBSessionInstallFlagKey]; // 使 App standardUserDefaults
// Keychain token
NSUserDefaults *installDefaults = [NSUserDefaults standardUserDefaults];
BOOL hasInitializedThisInstall = [installDefaults boolForKey:kKBSessionInstallFlagKey];
KBAuthManager *auth = [KBAuthManager shared]; // reloadFromKeychain KBAuthManager *auth = [KBAuthManager shared]; // reloadFromKeychain
if (!hasInitializedThisInstall) { if (!hasInitializedThisInstall) {
// //
[self.defaults setBool:YES forKey:kKBSessionInstallFlagKey]; [installDefaults setBool:YES forKey:kKBSessionInstallFlagKey];
[self.defaults synchronize]; [installDefaults synchronize];
// Keychain token // Keychain token
if (auth.current.accessToken.length > 0) { if (auth.current.accessToken.length > 0) {
@@ -160,4 +163,9 @@ static NSString * const kKBSessionInstallFlagKey = @"KBSession.installInitialize
} }
} }
- (void)goLoginVC{
KBLoginVC *vc = [[KBLoginVC alloc] init];
[KB_CURRENT_NAV pushViewController:vc animated:true];
}
@end @end

View File

@@ -14,6 +14,7 @@
[[KBNetworkManager shared] GET:API_LOGOUT [[KBNetworkManager shared] GET:API_LOGOUT
parameters:nil parameters:nil
headers:nil headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) { completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// HUD // HUD
[KBHUD dismiss]; [KBHUD dismiss];
@@ -34,9 +35,9 @@
// / // /
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate; id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
if ([appDelegate respondsToSelector:@selector(setupRootVC)]) { if ([appDelegate respondsToSelector:@selector(toMainTabbarVC)]) {
AppDelegate *delegate = (AppDelegate *)appDelegate; AppDelegate *delegate = (AppDelegate *)appDelegate;
[delegate setupRootVC]; [delegate toMainTabbarVC];
} }
}); });
}]; }];

View File

@@ -22,8 +22,8 @@ typedef NS_ERROR_ENUM(KBNetworkErrorDomain, KBNetworkError) {
/// JSON 回调:约定服务端统一返回顶层 NSDictionary{code,message,data,...} /// JSON 回调:约定服务端统一返回顶层 NSDictionary{code,message,data,...}
typedef void(^KBNetworkCompletion)(NSDictionary *_Nullable json, typedef void(^KBNetworkCompletion)(NSDictionary *_Nullable json,
NSURLResponse * _Nullable response, NSURLResponse *_Nullable response,
NSError * _Nullable error); NSError *_Nullable error);
/// 二进制回调:用于下载 zip、图片等原始数据 /// 二进制回调:用于下载 zip、图片等原始数据
typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data, typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
@@ -48,6 +48,13 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
@property (nonatomic, assign) NSTimeInterval timeout; @property (nonatomic, assign) NSTimeInterval timeout;
/// GET 请求parameters 会拼到 URL 上 /// GET 请求parameters 会拼到 URL 上
- (nullable NSURLSessionDataTask *)GET:(NSString *)path
parameters:(nullable NSDictionary *)parameters
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
autoShowBusinessError:(BOOL)autoShowBusinessError
completion:(KBNetworkCompletion)completion;
/// 兼容旧调用:默认 autoShowBusinessError = YES
- (nullable NSURLSessionDataTask *)GET:(NSString *)path - (nullable NSURLSessionDataTask *)GET:(NSString *)path
parameters:(nullable NSDictionary *)parameters parameters:(nullable NSDictionary *)parameters
headers:(nullable NSDictionary<NSString *, NSString *> *)headers headers:(nullable NSDictionary<NSString *, NSString *> *)headers
@@ -60,6 +67,13 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
completion:(KBNetworkDataCompletion)completion; completion:(KBNetworkDataCompletion)completion;
/// POST JSON 请求jsonBody 会以 application/json 发送 /// POST JSON 请求jsonBody 会以 application/json 发送
- (nullable NSURLSessionDataTask *)POST:(NSString *)path
jsonBody:(nullable id)jsonBody
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
autoShowBusinessError:(BOOL)autoShowBusinessError
completion:(KBNetworkCompletion)completion;
/// 兼容旧调用:默认 autoShowBusinessError = YES
- (nullable NSURLSessionDataTask *)POST:(NSString *)path - (nullable NSURLSessionDataTask *)POST:(NSString *)path
jsonBody:(nullable id)jsonBody jsonBody:(nullable id)jsonBody
headers:(nullable NSDictionary<NSString *, NSString *> *)headers headers:(nullable NSDictionary<NSString *, NSString *> *)headers

View File

@@ -53,12 +53,15 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
return self; return self;
} }
#
#pragma mark - Public #pragma mark - Public
// JSON GET
- (NSURLSessionDataTask *)GET:(NSString *)path - (NSURLSessionDataTask *)GET:(NSString *)path
parameters:(NSDictionary *)parameters parameters:(NSDictionary *)parameters
headers:(NSDictionary<NSString *,NSString *> *)headers headers:(NSDictionary<NSString *,NSString *> *)headers
completion:(KBNetworkCompletion)completion { autoShowBusinessError:(BOOL)autoShowBusinessError
completion:(KBNetworkCompletion)completion {
NSLog(@"[KBNetworkManager] GET called, enabled=%d, path=%@", self.isEnabled, path); NSLog(@"[KBNetworkManager] GET called, enabled=%d, path=%@", self.isEnabled, path);
if (![self ensureEnabled:completion]) return nil; if (![self ensureEnabled:completion]) return nil;
NSString *urlString = [self buildURLStringWithPath:path]; NSString *urlString = [self buildURLStringWithPath:path];
@@ -84,14 +87,30 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
req.allHTTPHeaderFields ?: @{}, req.allHTTPHeaderFields ?: @{},
paramStr); paramStr);
#endif #endif
return [self startJSONTaskWithRequest:req completion:completion]; return [self startJSONTaskWithRequest:req
autoShowBusinessError:autoShowBusinessError
completion:completion];
} }
// GET
- (NSURLSessionDataTask *)GET:(NSString *)path
parameters:(NSDictionary *)parameters
headers:(NSDictionary<NSString *,NSString *> *)headers
completion:(KBNetworkCompletion)completion {
return [self GET:path
parameters:parameters
headers:headers
autoShowBusinessError:YES
completion:completion];
}
// JSON POST
- (NSURLSessionDataTask *)POST:(NSString *)path - (NSURLSessionDataTask *)POST:(NSString *)path
jsonBody:(id)jsonBody jsonBody:(id)jsonBody
headers:(NSDictionary<NSString *,NSString *> *)headers headers:(NSDictionary<NSString *,NSString *> *)headers
completion:(KBNetworkCompletion)completion { autoShowBusinessError:(BOOL)autoShowBusinessError
NSLog(@"====="); completion:(KBNetworkCompletion)completion {
NSLog(@"[KBNetworkManager] POST called, enabled=%d, path=%@", self.isEnabled, path);
if (![self ensureEnabled:completion]) return nil; if (![self ensureEnabled:completion]) return nil;
NSString *urlString = [self buildURLStringWithPath:path]; NSString *urlString = [self buildURLStringWithPath:path];
if (!urlString) { [self fail:KBNetworkErrorInvalidURL completion:completion]; return nil; } if (!urlString) { [self fail:KBNetworkErrorInvalidURL completion:completion]; return nil; }
@@ -113,7 +132,21 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
req.allHTTPHeaderFields ?: @{}, req.allHTTPHeaderFields ?: @{},
bodyStr); bodyStr);
#endif #endif
return [self startJSONTaskWithRequest:req completion:completion]; return [self startJSONTaskWithRequest:req
autoShowBusinessError:autoShowBusinessError
completion:completion];
}
// POST
- (NSURLSessionDataTask *)POST:(NSString *)path
jsonBody:(id)jsonBody
headers:(NSDictionary<NSString *,NSString *> *)headers
completion:(KBNetworkCompletion)completion {
return [self POST:path
jsonBody:jsonBody
headers:headers
autoShowBusinessError:YES
completion:completion];
} }
// GET zip // GET zip
@@ -190,6 +223,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
} }
- (NSURLSessionDataTask *)startJSONTaskWithRequest:(NSURLRequest *)req - (NSURLSessionDataTask *)startJSONTaskWithRequest:(NSURLRequest *)req
autoShowBusinessError:(BOOL)autoShowBusinessError
completion:(KBNetworkCompletion)completion { completion:(KBNetworkCompletion)completion {
NSLog(@"[KBNetworkManager] startAFTaskWithRequest: %@", req.URL.absoluteString); NSLog(@"[KBNetworkManager] startAFTaskWithRequest: %@", req.URL.absoluteString);
// Content-Type JSON // Content-Type JSON
@@ -266,18 +300,23 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
// code { code, message, data } // code { code, message, data }
NSInteger bizCode = KBBizCodeFromJSONObject(dict); NSInteger bizCode = KBBizCodeFromJSONObject(dict);
if (bizCode != NSNotFound && bizCode != KBBizCodeSuccess) { if (bizCode != NSNotFound && bizCode != KBBizCodeSuccess) {
// code token error // code token error
[self kb_handleBizCode:bizCode json:dict response:response]; BOOL handledByAuth = [self kb_handleBizCode:bizCode json:dict response:response];
NSString *msg = KBBizMessageFromJSONObject(json) ?: KBLocalized(@"Server error"); NSString *msg = KBBizMessageFromJSONObject(dict) ?: KBLocalized(@"Server error");
NSError *bizErr = [NSError errorWithDomain:KBNetworkErrorDomain if (autoShowBusinessError && !handledByAuth) {
code:KBNetworkErrorBusiness dispatch_async(dispatch_get_main_queue(), ^{
userInfo:@{ [KBHUD showInfo:msg];
NSLocalizedDescriptionKey : msg, });
@"code" : @(bizCode)
}];
if (completion) completion(dict, response, bizErr);
return;
} }
NSError *bizErr = [NSError errorWithDomain:KBNetworkErrorDomain
code:KBNetworkErrorBusiness
userInfo:@{
NSLocalizedDescriptionKey : msg,
@"code" : @(bizCode)
}];
if (completion) completion(dict, response, bizErr);
return;
}
// code // code
if (completion) completion(dict, response, nil); if (completion) completion(dict, response, nil);
} else { } else {
@@ -328,8 +367,9 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
#pragma mark - Private helpers #pragma mark - Private helpers
/// codetoken 线 /// codetoken 线
- (void)kb_handleBizCode:(NSInteger)bizCode /// YES code
- (BOOL)kb_handleBizCode:(NSInteger)bizCode
json:(id)json json:(id)json
response:(NSURLResponse *)response { response:(NSURLResponse *)response {
switch (bizCode) { switch (bizCode) {
@@ -361,10 +401,12 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
object:nil object:nil
userInfo:info]; userInfo:info];
}); });
return YES;
} break; } break;
default: default:
break; break;
} }
return NO;
} }
- (void)fail:(KBNetworkError)code completion:(KBNetworkCompletion)completion { - (void)fail:(KBNetworkError)code completion:(KBNetworkCompletion)completion {