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>
@property (nonatomic, strong) UIWindow *window;
- (void)setupRootVC;
- (void)toMainTabbarVC;
@end

View File

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

View File

@@ -164,6 +164,11 @@
#pragma mark - Actions
- (void)onTapBuyAction {
// if (self.onTapBuy) { self.onTapBuy(); }
//
if (![KBUserSessionManager shared].isLoggedIn) {
[[KBUserSessionManager shared] goLoginVC];
return;
}
KBVipPay *vc = [[KBVipPay alloc] init];
[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 *topRightImageView; // login_jianp_icon
@property (nonatomic, strong) UIButton *backButton; //
// 26
@property (nonatomic, strong) UIView *contentContainerView;
@@ -74,6 +75,14 @@
[self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) {
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];
@@ -165,7 +174,7 @@
id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
if ([appDelegate respondsToSelector:@selector(setupRootVC)]) {
AppDelegate *delegate = (AppDelegate *)appDelegate;
[delegate setupRootVC];
[delegate toMainTabbarVC];
}
});
} else {
@@ -195,6 +204,15 @@
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
- (UIImageView *)bgImageView {
@@ -223,6 +241,21 @@
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 {
if (!_titleLabel) {
_titleLabel = [UILabel new];

View File

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

View File

@@ -10,7 +10,7 @@
#import "KBUser.h"
#import "KBConfig.h"
#import <MJExtension/MJExtension.h>
#import "KBLoginVC.h"
/// App Group key
static NSString * const kKBSessionUserStoreKey = @"KBSession.currentUser";
/// key
@@ -58,14 +58,17 @@ static NSString * const kKBSessionInstallFlagKey = @"KBSession.installInitialize
if (self.didBootstrap) return;
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
if (!hasInitializedThisInstall) {
//
[self.defaults setBool:YES forKey:kKBSessionInstallFlagKey];
[self.defaults synchronize];
[installDefaults setBool:YES forKey:kKBSessionInstallFlagKey];
[installDefaults synchronize];
// Keychain token
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

View File

@@ -14,6 +14,7 @@
[[KBNetworkManager shared] GET:API_LOGOUT
parameters:nil
headers:nil
autoShowBusinessError:NO
completion:^(NSDictionary *jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// HUD
[KBHUD dismiss];
@@ -34,9 +35,9 @@
// /
dispatch_async(dispatch_get_main_queue(), ^{
id<UIApplicationDelegate> appDelegate = UIApplication.sharedApplication.delegate;
if ([appDelegate respondsToSelector:@selector(setupRootVC)]) {
if ([appDelegate respondsToSelector:@selector(toMainTabbarVC)]) {
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,...}
typedef void(^KBNetworkCompletion)(NSDictionary *_Nullable json,
NSURLResponse * _Nullable response,
NSError * _Nullable error);
NSURLResponse *_Nullable response,
NSError *_Nullable error);
/// 二进制回调:用于下载 zip、图片等原始数据
typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
@@ -48,6 +48,13 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
@property (nonatomic, assign) NSTimeInterval timeout;
/// 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
parameters:(nullable NSDictionary *)parameters
headers:(nullable NSDictionary<NSString *, NSString *> *)headers
@@ -60,6 +67,13 @@ typedef void(^KBNetworkDataCompletion)(NSData *_Nullable data,
completion:(KBNetworkDataCompletion)completion;
/// 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
jsonBody:(nullable id)jsonBody
headers:(nullable NSDictionary<NSString *, NSString *> *)headers

View File

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