3
This commit is contained in:
@@ -27,7 +27,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
|||||||
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
||||||
_timeout = 10.0;
|
_timeout = 10.0;
|
||||||
// 默认请求头:Accept 任意类型 + 使用项目多语言管理器设置 Accept-Language
|
// 默认请求头:Accept 任意类型 + 使用项目多语言管理器设置 Accept-Language
|
||||||
NSString *lang = [KBLocalizationManager shared].currentLanguageCode ?: @"en";
|
NSString *lang = [KBLocalizationManager shared].currentLanguageCode ?: KBLanguageCodeEnglish;
|
||||||
_defaultHeaders = @{
|
_defaultHeaders = @{
|
||||||
@"Accept": @"*/*",
|
@"Accept": @"*/*",
|
||||||
@"Accept-Language": lang
|
@"Accept-Language": lang
|
||||||
|
|||||||
@@ -11,6 +11,16 @@
|
|||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/// 语言码类型(集中管理,避免在各处散落写 @"en"/@"zh-Hans")
|
||||||
|
typedef NSString *KBLanguageCode NS_EXTENSIBLE_STRING_ENUM;
|
||||||
|
|
||||||
|
/// 项目内统一使用的语言常量
|
||||||
|
FOUNDATION_EXPORT KBLanguageCode const KBLanguageCodeEnglish; // @"en"
|
||||||
|
FOUNDATION_EXPORT KBLanguageCode const KBLanguageCodeSimplifiedChinese; // @"zh-Hans"
|
||||||
|
|
||||||
|
/// 默认支持的语言列表(目前为 @[KBLanguageCodeEnglish, KBLanguageCodeSimplifiedChinese])
|
||||||
|
FOUNDATION_EXPORT NSArray<KBLanguageCode> *KBDefaultSupportedLanguageCodes(void);
|
||||||
|
|
||||||
/// 当前语言变更通知(不附带 userInfo)
|
/// 当前语言变更通知(不附带 userInfo)
|
||||||
extern NSString * const KBLocalizationDidChangeNotification;
|
extern NSString * const KBLocalizationDidChangeNotification;
|
||||||
|
|
||||||
@@ -20,18 +30,18 @@ extern NSString * const KBLocalizationDidChangeNotification;
|
|||||||
/// 单例
|
/// 单例
|
||||||
+ (instancetype)shared;
|
+ (instancetype)shared;
|
||||||
|
|
||||||
/// 当前语言代码(如:"en"、"zh-Hans"、"ja")。
|
/// 当前语言代码(如:KBLanguageCodeEnglish、KBLanguageCodeSimplifiedChinese)。
|
||||||
/// 默认会在受支持语言中,按系统首选语言择优匹配。
|
/// 默认会在受支持语言中,按系统首选语言择优匹配。
|
||||||
@property (nonatomic, copy, readonly) NSString *currentLanguageCode;
|
@property (nonatomic, copy, readonly) KBLanguageCode currentLanguageCode;
|
||||||
|
|
||||||
/// 支持的语言代码列表。默认 @[@"en", @"zh-Hans"]。
|
/// 支持的语言代码列表。默认 `KBDefaultSupportedLanguageCodes()`。
|
||||||
/// 建议在启动早期设置;或设置后再调用 `-setCurrentLanguageCode:persist:` 以刷新。
|
/// 建议在启动早期设置;或设置后再调用 `-setCurrentLanguageCode:persist:` 以刷新。
|
||||||
@property (nonatomic, copy) NSArray<NSString *> *supportedLanguageCodes;
|
@property (nonatomic, copy) NSArray<KBLanguageCode> *supportedLanguageCodes;
|
||||||
|
|
||||||
/// 设置当前语言。
|
/// 设置当前语言。
|
||||||
/// @param code 语言代码
|
/// @param code 语言代码
|
||||||
/// @param persist 是否持久化到共享钥匙串(以便 App 与扩展共享该选择)
|
/// @param persist 是否持久化到共享钥匙串(以便 App 与扩展共享该选择)
|
||||||
- (void)setCurrentLanguageCode:(NSString *)code persist:(BOOL)persist;
|
- (void)setCurrentLanguageCode:(KBLanguageCode)code persist:(BOOL)persist;
|
||||||
|
|
||||||
/// 清除用户选择,恢复为系统最佳匹配。
|
/// 清除用户选择,恢复为系统最佳匹配。
|
||||||
- (void)resetToSystemLanguage;
|
- (void)resetToSystemLanguage;
|
||||||
@@ -45,7 +55,7 @@ extern NSString * const KBLocalizationDidChangeNotification;
|
|||||||
value:(nullable NSString *)value;
|
value:(nullable NSString *)value;
|
||||||
|
|
||||||
/// 基于一组“偏好语言”计算最佳支持语言。
|
/// 基于一组“偏好语言”计算最佳支持语言。
|
||||||
- (NSString *)bestSupportedLanguageForPreferred:(NSArray<NSString *> *)preferred;
|
- (KBLanguageCode)bestSupportedLanguageForPreferred:(NSArray<NSString *> *)preferred;
|
||||||
|
|
||||||
- (void)reloadFromSharedStorageIfNeeded;
|
- (void)reloadFromSharedStorageIfNeeded;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,20 @@
|
|||||||
#import <Security/Security.h>
|
#import <Security/Security.h>
|
||||||
#import "KBConfig.h"
|
#import "KBConfig.h"
|
||||||
|
|
||||||
|
/// 语言常量定义
|
||||||
|
KBLanguageCode const KBLanguageCodeEnglish = @"en";
|
||||||
|
KBLanguageCode const KBLanguageCodeSimplifiedChinese = @"zh-Hans";
|
||||||
|
|
||||||
|
/// 默认支持语言列表(集中配置)
|
||||||
|
NSArray<KBLanguageCode> *KBDefaultSupportedLanguageCodes(void) {
|
||||||
|
static NSArray<KBLanguageCode> *codes;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once(&onceToken, ^{
|
||||||
|
codes = @[KBLanguageCodeEnglish, KBLanguageCodeSimplifiedChinese];
|
||||||
|
});
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
/// 语言变更通知名称
|
/// 语言变更通知名称
|
||||||
NSString * const KBLocalizationDidChangeNotification = @"KBLocalizationDidChangeNotification";
|
NSString * const KBLocalizationDidChangeNotification = @"KBLocalizationDidChangeNotification";
|
||||||
|
|
||||||
@@ -15,7 +29,7 @@ static NSString * const kKBLocService = @"com.loveKey.nyx.loc";
|
|||||||
static NSString * const kKBLocAccount = @"lang"; // 保存 UTF8 的语言代码
|
static NSString * const kKBLocAccount = @"lang"; // 保存 UTF8 的语言代码
|
||||||
|
|
||||||
@interface KBLocalizationManager ()
|
@interface KBLocalizationManager ()
|
||||||
@property (nonatomic, copy, readwrite) NSString *currentLanguageCode; // 当前语言代码
|
@property (nonatomic, copy, readwrite) KBLanguageCode currentLanguageCode; // 当前语言代码
|
||||||
@property (nonatomic, strong) NSBundle *langBundle; // 对应语言的 .lproj 资源包
|
@property (nonatomic, strong) NSBundle *langBundle; // 对应语言的 .lproj 资源包
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -37,11 +51,11 @@ static inline NSMutableDictionary *KBLocBaseKCQuery(void) {
|
|||||||
static KBLocalizationManager *m; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
|
static KBLocalizationManager *m; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{
|
||||||
m = [KBLocalizationManager new];
|
m = [KBLocalizationManager new];
|
||||||
// 默认支持语言;可在启动时由外部重置
|
// 默认支持语言;可在启动时由外部重置
|
||||||
m.supportedLanguageCodes = @[ @"en", @"zh-Hans" ];
|
m.supportedLanguageCodes = KBDefaultSupportedLanguageCodes();
|
||||||
// 启动读取:先取共享钥匙串,再按系统偏好回退
|
// 启动读取:先取共享钥匙串,再按系统偏好回退
|
||||||
NSString *saved = [[self class] kc_read];
|
NSString *saved = [[self class] kc_read];
|
||||||
if (saved.length == 0) {
|
if (saved.length == 0) {
|
||||||
saved = [m bestSupportedLanguageForPreferred:[NSLocale preferredLanguages]] ?: @"en";
|
saved = [m bestSupportedLanguageForPreferred:[NSLocale preferredLanguages]] ?: KBLanguageCodeEnglish;
|
||||||
}
|
}
|
||||||
[m applyLanguage:saved];
|
[m applyLanguage:saved];
|
||||||
});
|
});
|
||||||
@@ -56,7 +70,7 @@ static inline NSMutableDictionary *KBLocBaseKCQuery(void) {
|
|||||||
for (NSString *c in supportedLanguageCodes) {
|
for (NSString *c in supportedLanguageCodes) {
|
||||||
if (c.length) { [set addObject:c]; }
|
if (c.length) { [set addObject:c]; }
|
||||||
}
|
}
|
||||||
_supportedLanguageCodes = set.array.count ? set.array : @[ @"en" ];
|
_supportedLanguageCodes = set.array.count ? (NSArray<KBLanguageCode> *)set.array : @[ KBLanguageCodeEnglish ];
|
||||||
// 若当前语言不再受支持,则按最佳匹配切回(不持久化,仅内存),并广播变更
|
// 若当前语言不再受支持,则按最佳匹配切回(不持久化,仅内存),并广播变更
|
||||||
if (self.currentLanguageCode.length && ![set containsObject:self.currentLanguageCode]) {
|
if (self.currentLanguageCode.length && ![set containsObject:self.currentLanguageCode]) {
|
||||||
NSString *best = [self bestSupportedLanguageForPreferred:@[self.currentLanguageCode]];
|
NSString *best = [self bestSupportedLanguageForPreferred:@[self.currentLanguageCode]];
|
||||||
@@ -74,7 +88,7 @@ static inline NSMutableDictionary *KBLocBaseKCQuery(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)resetToSystemLanguage {
|
- (void)resetToSystemLanguage {
|
||||||
NSString *best = [self bestSupportedLanguageForPreferred:[NSLocale preferredLanguages]] ?: @"en";
|
NSString *best = [self bestSupportedLanguageForPreferred:[NSLocale preferredLanguages]] ?: KBLanguageCodeEnglish;
|
||||||
[self setCurrentLanguageCode:best persist:NO];
|
[self setCurrentLanguageCode:best persist:NO];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +106,7 @@ static inline NSMutableDictionary *KBLocBaseKCQuery(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (NSString *)bestSupportedLanguageForPreferred:(NSArray<NSString *> *)preferred {
|
- (NSString *)bestSupportedLanguageForPreferred:(NSArray<NSString *> *)preferred {
|
||||||
if (self.supportedLanguageCodes.count == 0) return @"en";
|
if (self.supportedLanguageCodes.count == 0) return KBLanguageCodeEnglish;
|
||||||
// 1) 完全匹配
|
// 1) 完全匹配
|
||||||
for (NSString *p in preferred) {
|
for (NSString *p in preferred) {
|
||||||
NSString *pLC = p.lowercaseString;
|
NSString *pLC = p.lowercaseString;
|
||||||
@@ -129,7 +143,7 @@ static inline NSMutableDictionary *KBLocBaseKCQuery(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 4) 兜底:取第一个受支持语言
|
// 4) 兜底:取第一个受支持语言
|
||||||
return self.supportedLanguageCodes.firstObject ?: @"en";
|
return self.supportedLanguageCodes.firstObject ?: KBLanguageCodeEnglish;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - 内部实现
|
#pragma mark - 内部实现
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
// 1. 配置内购服务
|
// 1. 配置内购服务
|
||||||
[[FGIAPManager shared] setConfigureWith:[IAPVerifyTransactionObj new]];
|
[[FGIAPManager shared] setConfigureWith:[IAPVerifyTransactionObj new]];
|
||||||
/// 2:配置国际化
|
/// 2:配置国际化(统一使用集中管理的语言列表)
|
||||||
[KBLocalizationManager shared].supportedLanguageCodes = @[ @"en", @"zh-Hans"];
|
[KBLocalizationManager shared].supportedLanguageCodes = KBDefaultSupportedLanguageCodes();
|
||||||
/// 3 : 处理token问题
|
/// 3 : 处理token问题
|
||||||
// [[KBUserSessionManager shared] bootstrapIfNeeded];
|
// [[KBUserSessionManager shared] bootstrapIfNeeded];
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,10 @@
|
|||||||
if (!self) return;
|
if (!self) return;
|
||||||
|
|
||||||
KBLocalizationManager *mgr = [KBLocalizationManager shared];
|
KBLocalizationManager *mgr = [KBLocalizationManager shared];
|
||||||
NSString *next = [mgr.currentLanguageCode.lowercaseString hasPrefix:@"zh"] ? @"en" : @"zh-Hans";
|
// 在中英文间切换:当前是中文就切到英文,否则切到简体中文
|
||||||
|
NSString *next = [mgr.currentLanguageCode.lowercaseString hasPrefix:@"zh"]
|
||||||
|
? KBLanguageCodeEnglish
|
||||||
|
: KBLanguageCodeSimplifiedChinese;
|
||||||
[mgr setCurrentLanguageCode:next persist:YES];
|
[mgr setCurrentLanguageCode:next persist:YES];
|
||||||
|
|
||||||
// 语言切换后重新进入根控制器
|
// 语言切换后重新进入根控制器
|
||||||
|
|||||||
@@ -11,22 +11,16 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
// 标识
|
// 标识
|
||||||
@property (nonatomic, copy, nullable) NSString *userId; // id/user_id/uid
|
@property (nonatomic, copy, nullable) NSString *userId; // id/user_id/uid
|
||||||
@property (nonatomic, copy, nullable) NSString *appleUserId; // 用 Apple 登录返回的 userID(可选)
|
|
||||||
|
|
||||||
// 基本信息
|
// 基本信息
|
||||||
@property (nonatomic, copy, nullable) NSString *nickname;
|
@property (nonatomic, copy, nullable) NSString *nickName;
|
||||||
@property (nonatomic, copy, nullable) NSString *avatar; // 头像 URL
|
@property (nonatomic, copy, nullable) NSString *avatarUrl; // 头像 URL
|
||||||
@property (nonatomic, copy, nullable) NSString *gender; // 性别(后端可能返回 string/int,统一转字符串存)
|
@property (nonatomic, copy, nullable) NSString *gender; // 性别(后端可能返回 string/int,统一转字符串存)
|
||||||
@property (nonatomic, copy, nullable) NSString *mobile;
|
|
||||||
@property (nonatomic, copy, nullable) NSString *email;
|
@property (nonatomic, copy, nullable) NSString *email;
|
||||||
|
/// 邮箱是否验证
|
||||||
|
@property (nonatomic, assign) BOOL emailVerified;
|
||||||
|
|
||||||
// 会话信息
|
// 会话信息
|
||||||
@property (nonatomic, copy, nullable) NSString *token; // token/access_token/accessToken
|
@property (nonatomic, copy, nullable) NSString *token; // token/access_token/accessToken
|
||||||
@property (nonatomic, copy, nullable) NSString *refreshToken; // refresh_token/refreshToken
|
|
||||||
@property (nonatomic, strong, nullable) NSDate *expiryDate; // 若后端返回过期时间,转为日期
|
|
||||||
|
|
||||||
/// 从后端返回(可能顶层或 data/user 嵌套)中解析用户模型。内部使用 MJExtension。
|
|
||||||
+ (instancetype)userFromResponseObject:(id)jsonObject;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -9,89 +9,10 @@
|
|||||||
|
|
||||||
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
+ (NSDictionary *)mj_replacedKeyFromPropertyName {
|
||||||
return @{
|
return @{
|
||||||
@"userId": @[@"id", @"user_id", @"uid"],
|
@"userId": @[@"uid"],
|
||||||
@"appleUserId": @[@"appleUserId", @"apple_user_id", @"apple_userid", @"appleUserID"],
|
|
||||||
@"nickname": @[@"nickname", @"nick", @"name"],
|
|
||||||
@"avatar": @[@"avatar", @"avatar_url", @"head", @"headimg"],
|
|
||||||
@"gender": @[@"gender", @"sex"],
|
@"gender": @[@"gender", @"sex"],
|
||||||
@"mobile": @[@"mobile", @"phone"],
|
|
||||||
@"email": @[@"email"],
|
|
||||||
@"token": @[@"token", @"access_token", @"accessToken"],
|
|
||||||
@"refreshToken": @[@"refresh_token", @"refreshToken"],
|
|
||||||
@"expiryDate": @[@"expire_at", @"expireAt", @"expires_at", @"expiresAt", @"expired_at"],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将可能是字符串/时间戳(秒/毫秒)的过期时间,转为 NSDate
|
|
||||||
- (void)setExpiryDate:(NSDate *)expiryDate { _expiryDate = expiryDate; }
|
|
||||||
|
|
||||||
+ (instancetype)userFromResponseObject:(id)jsonObject {
|
|
||||||
if (!jsonObject) return nil;
|
|
||||||
NSDictionary *dict = nil;
|
|
||||||
if ([jsonObject isKindOfClass:NSDictionary.class]) {
|
|
||||||
dict = (NSDictionary *)jsonObject;
|
|
||||||
} else if ([jsonObject isKindOfClass:NSData.class]) {
|
|
||||||
// JSON data -> dict
|
|
||||||
id obj = [NSJSONSerialization JSONObjectWithData:(NSData *)jsonObject options:0 error:NULL];
|
|
||||||
if ([obj isKindOfClass:NSDictionary.class]) dict = obj;
|
|
||||||
}
|
|
||||||
if (!dict) return nil;
|
|
||||||
|
|
||||||
// 兼容多种后端包装:优先 data.user,其次 data,本身就是用户
|
|
||||||
NSDictionary *candidate = nil;
|
|
||||||
id data = dict[@"data"]; if ([data isKindOfClass:NSDictionary.class]) { candidate = data; }
|
|
||||||
id user = [candidate objectForKey:@"user"]; if (![user isKindOfClass:NSDictionary.class]) { user = dict[@"user"]; }
|
|
||||||
NSDictionary *userDict = ([user isKindOfClass:NSDictionary.class]) ? (NSDictionary *)user : (candidate ?: dict);
|
|
||||||
|
|
||||||
KBUser *u = [KBUser mj_objectWithKeyValues:userDict];
|
|
||||||
|
|
||||||
// 额外兼容:若 token 不在用户对象里,尝试从上层提取到模型上
|
|
||||||
if (u.token.length == 0) {
|
|
||||||
NSString *t = [self pickTokenFromDictionary:dict];
|
|
||||||
if (t.length) u.token = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过期时间字段可能是字符串或时间戳,做宽松转换
|
|
||||||
id exp = userDict[@"expire_at"] ?: userDict[@"expireAt"] ?: userDict[@"expires_at"] ?: userDict[@"expiresAt"] ?: userDict[@"expired_at"];
|
|
||||||
if ([exp isKindOfClass:NSNumber.class]) {
|
|
||||||
// 兼容秒/毫秒(> 10^11 视为毫秒)
|
|
||||||
NSTimeInterval ts = [(NSNumber *)exp doubleValue];
|
|
||||||
if (ts > 1e11) ts = ts / 1000.0;
|
|
||||||
u.expiryDate = [NSDate dateWithTimeIntervalSince1970:ts];
|
|
||||||
} else if ([exp isKindOfClass:NSString.class]) {
|
|
||||||
// 尝试 ISO8601
|
|
||||||
NSDateFormatter *fmt = [NSDateFormatter new];
|
|
||||||
fmt.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
|
||||||
fmt.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
|
|
||||||
NSDate *d = [fmt dateFromString:(NSString *)exp];
|
|
||||||
if (!d) {
|
|
||||||
// 尝试纯秒
|
|
||||||
NSTimeInterval ts = [(NSString *)exp doubleValue];
|
|
||||||
if (ts > 0) d = [NSDate dateWithTimeIntervalSince1970:ts];
|
|
||||||
}
|
|
||||||
if (d) u.expiryDate = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString *)pickTokenFromDictionary:(NSDictionary *)dict {
|
|
||||||
if (![dict isKindOfClass:NSDictionary.class]) return nil;
|
|
||||||
NSString *(^pick)(NSDictionary *) = ^NSString *(NSDictionary *d) {
|
|
||||||
NSArray *keys = @[ @"token", @"access_token", @"accessToken" ];
|
|
||||||
for (NSString *k in keys) {
|
|
||||||
id v = d[k]; if ([v isKindOfClass:NSString.class] && ((NSString *)v).length > 0) return v;
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
};
|
|
||||||
NSString *t = pick(dict); if (t.length) return t;
|
|
||||||
id data = dict[@"data"]; if ([data isKindOfClass:NSDictionary.class]) { t = pick(data); if (t.length) return t; }
|
|
||||||
id user = dict[@"user"]; if ([user isKindOfClass:NSDictionary.class]) { t = pick(user); if (t.length) return t; }
|
|
||||||
NSDictionary *d2 = dict[@"data"]; if ([d2 isKindOfClass:NSDictionary.class]) {
|
|
||||||
NSDictionary *session = d2[@"session"]; if ([session isKindOfClass:NSDictionary.class]) { t = pick(session); if (t.length) return t; }
|
|
||||||
}
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -117,9 +117,9 @@
|
|||||||
// 底部协议文案(单个富文本视图,内部 terms/privacy 文案可点击)
|
// 底部协议文案(单个富文本视图,内部 terms/privacy 文案可点击)
|
||||||
[self.contentContainerView addSubview:self.agreementTextView];
|
[self.contentContainerView addSubview:self.agreementTextView];
|
||||||
[self.agreementTextView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.agreementTextView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.centerX.equalTo(self.contentContainerView);
|
// 协议文案占满容器左右 30 的留白,配合 textAlignmentCenter 实现真正水平居中
|
||||||
make.left.greaterThanOrEqualTo(self.contentContainerView).offset(30);
|
make.left.equalTo(self.contentContainerView).offset(30);
|
||||||
make.right.lessThanOrEqualTo(self.contentContainerView).offset(-30);
|
make.right.equalTo(self.contentContainerView).offset(-30);
|
||||||
make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 84);
|
make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 84);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
@@ -128,14 +128,14 @@
|
|||||||
[self.contentContainerView addSubview:forgot];
|
[self.contentContainerView addSubview:forgot];
|
||||||
[forgot mas_makeConstraints:^(MASConstraintMaker *make) {
|
[forgot mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.centerX.equalTo(self.contentContainerView);
|
make.centerX.equalTo(self.contentContainerView);
|
||||||
make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 24);
|
make.bottom.equalTo(self.contentContainerView).offset(-KB_SAFE_BOTTOM - 10);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
UIView *accountLine = [UIView new];
|
UIView *accountLine = [UIView new];
|
||||||
[self.contentContainerView addSubview:accountLine];
|
[self.contentContainerView addSubview:accountLine];
|
||||||
[accountLine mas_makeConstraints:^(MASConstraintMaker *make) {
|
[accountLine mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
make.centerX.equalTo(self.contentContainerView);
|
make.centerX.equalTo(self.contentContainerView);
|
||||||
make.bottom.equalTo(forgot.mas_top).offset(-8);
|
make.bottom.equalTo(forgot.mas_top).offset(-2);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[accountLine addSubview:self.noAccountLabel];
|
[accountLine addSubview:self.noAccountLabel];
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
_emailLoginButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
_emailLoginButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
[_emailLoginButton setTitle:KBLocalized(@"Continue Via Email") forState:UIControlStateNormal];
|
[_emailLoginButton setTitle:KBLocalized(@"Continue Via Email") forState:UIControlStateNormal];
|
||||||
[_emailLoginButton setTitleColor:[UIColor colorWithHex:KBBlackValue] forState:UIControlStateNormal];
|
[_emailLoginButton setTitleColor:[UIColor colorWithHex:KBBlackValue] forState:UIControlStateNormal];
|
||||||
_emailLoginButton.titleLabel.font = [KBFont medium:16];
|
_emailLoginButton.titleLabel.font = [KBFont medium:19];
|
||||||
// _emailLoginButton.backgroundColor = [UIColor colorWithHex:0xF7F7F7];
|
// _emailLoginButton.backgroundColor = [UIColor colorWithHex:0xF7F7F7];
|
||||||
_emailLoginButton.layer.cornerRadius = 10.0;
|
_emailLoginButton.layer.cornerRadius = 10.0;
|
||||||
_emailLoginButton.layer.masksToBounds = YES;
|
_emailLoginButton.layer.masksToBounds = YES;
|
||||||
@@ -291,10 +291,14 @@
|
|||||||
NSString *termsText = @"terms of service";
|
NSString *termsText = @"terms of service";
|
||||||
NSString *privacyText = @"privacy policy";
|
NSString *privacyText = @"privacy policy";
|
||||||
|
|
||||||
|
NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
|
||||||
|
paragraph.alignment = NSTextAlignmentCenter; // 多行文本整体居中
|
||||||
|
|
||||||
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:fullText
|
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:fullText
|
||||||
attributes:@{
|
attributes:@{
|
||||||
NSFontAttributeName : [KBFont regular:12],
|
NSFontAttributeName : [KBFont regular:10],
|
||||||
NSForegroundColorAttributeName : [UIColor colorWithHex:0x717171]
|
NSForegroundColorAttributeName : [UIColor colorWithHex:0x717171],
|
||||||
|
NSParagraphStyleAttributeName : paragraph
|
||||||
}];
|
}];
|
||||||
|
|
||||||
NSString *lowerFull = fullText.lowercaseString;
|
NSString *lowerFull = fullText.lowercaseString;
|
||||||
@@ -332,8 +336,8 @@
|
|||||||
if (!_noAccountLabel) {
|
if (!_noAccountLabel) {
|
||||||
_noAccountLabel = [UILabel new];
|
_noAccountLabel = [UILabel new];
|
||||||
_noAccountLabel.text = KBLocalized(@"Don't Have An Account?");
|
_noAccountLabel.text = KBLocalized(@"Don't Have An Account?");
|
||||||
_noAccountLabel.font = [KBFont regular:13];
|
_noAccountLabel.font = [KBFont regular:10];
|
||||||
_noAccountLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0];
|
_noAccountLabel.textColor = [UIColor colorWithHex:KBBlackValue];
|
||||||
}
|
}
|
||||||
return _noAccountLabel;
|
return _noAccountLabel;
|
||||||
}
|
}
|
||||||
@@ -343,7 +347,7 @@
|
|||||||
_signUpButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
_signUpButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
[_signUpButton setTitle:KBLocalized(@"Sign Up") forState:UIControlStateNormal];
|
[_signUpButton setTitle:KBLocalized(@"Sign Up") forState:UIControlStateNormal];
|
||||||
[_signUpButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal];
|
[_signUpButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal];
|
||||||
_signUpButton.titleLabel.font = [KBFont medium:13];
|
_signUpButton.titleLabel.font = [KBFont medium:10];
|
||||||
[_signUpButton addTarget:self action:@selector(onTapSignUp) forControlEvents:UIControlEventTouchUpInside];
|
[_signUpButton addTarget:self action:@selector(onTapSignUp) forControlEvents:UIControlEventTouchUpInside];
|
||||||
}
|
}
|
||||||
return _signUpButton;
|
return _signUpButton;
|
||||||
@@ -354,7 +358,7 @@
|
|||||||
_forgotPasswordButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
_forgotPasswordButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||||
[_forgotPasswordButton setTitle:KBLocalized(@"Forgot Password?") forState:UIControlStateNormal];
|
[_forgotPasswordButton setTitle:KBLocalized(@"Forgot Password?") forState:UIControlStateNormal];
|
||||||
[_forgotPasswordButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal];
|
[_forgotPasswordButton setTitleColor:[UIColor colorWithHex:KBColorValue] forState:UIControlStateNormal];
|
||||||
_forgotPasswordButton.titleLabel.font = [KBFont regular:13];
|
_forgotPasswordButton.titleLabel.font = [KBFont regular:10];
|
||||||
[_forgotPasswordButton addTarget:self action:@selector(onTapForgotPassword) forControlEvents:UIControlEventTouchUpInside];
|
[_forgotPasswordButton addTarget:self action:@selector(onTapForgotPassword) forControlEvents:UIControlEventTouchUpInside];
|
||||||
}
|
}
|
||||||
return _forgotPasswordButton;
|
return _forgotPasswordButton;
|
||||||
|
|||||||
@@ -43,27 +43,29 @@
|
|||||||
if (identityToken.length) params[@"identityToken"] = identityToken;
|
if (identityToken.length) params[@"identityToken"] = identityToken;
|
||||||
if (authorizationCode.length) params[@"accessCode"] = authorizationCode; // 仅供后端需要时使用
|
if (authorizationCode.length) params[@"accessCode"] = authorizationCode; // 仅供后端需要时使用
|
||||||
if (cred.user.length) params[@"userID"] = cred.user; // 可选
|
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:^(id _Nullable jsonOrData, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||||
|
[KBHUD dismiss];
|
||||||
if (error) { if (completion) completion(NO, error); return; }
|
if (error) { if (completion) completion(NO, error); return; }
|
||||||
|
|
||||||
// 从返回 JSON 中提取 token(支持常见命名与层级 data/user)
|
// 从返回 JSON 中提取 token(支持常见命名与层级 data/user)
|
||||||
// 先解析用户模型(使用 MJExtension)
|
// 先解析用户模型(使用 MJExtension)
|
||||||
KBUser *user = [KBUser userFromResponseObject:jsonOrData];
|
NSDictionary *dict = jsonOrData[@"data"];
|
||||||
|
KBUser *user = [KBUser mj_objectWithKeyValues:dict];
|
||||||
self.currentUser = user;
|
self.currentUser = user;
|
||||||
NSString *token = user.token ?: [self.class tokenFromResponseObject:jsonOrData];
|
if (user.token.length == 0) {
|
||||||
if (token.length == 0) {
|
|
||||||
if (completion) completion(NO, [NSError errorWithDomain:@"KBLogin" code:-2 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"No token returned")}]);
|
if (completion) completion(NO, [NSError errorWithDomain:@"KBLogin" code:-2 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"No token returned")}]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
[[KBUserSessionManager shared] handleLoginSuccessWithUser:user];
|
||||||
|
completion(true,nil);
|
||||||
// 保存登录态到共享钥匙串;供 App 与扩展共享
|
// 保存登录态到共享钥匙串;供 App 与扩展共享
|
||||||
BOOL ok = [[KBAuthManager shared] saveAccessToken:token
|
// BOOL ok = [[KBAuthManager shared] saveAccessToken:user.token
|
||||||
refreshToken:nil
|
// refreshToken:nil
|
||||||
expiryDate:nil
|
// expiryDate:nil
|
||||||
userIdentifier:cred.user];
|
// userIdentifier:cred.user];
|
||||||
if (completion) completion(ok, ok ? nil : [NSError errorWithDomain:@"KBLogin" code:-3 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Failed to save login state")}]);
|
// if (completion) completion(ok, ok ? nil : [NSError errorWithDomain:@"KBLogin" code:-3 userInfo:@{NSLocalizedDescriptionKey: KBLocalized(@"Failed to save login state")}]);
|
||||||
}];
|
}];
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,9 +97,9 @@ static NSString * const kKBSessionInstallFlagKey = @"KBSession.installInitialize
|
|||||||
NSString *token = user.token;
|
NSString *token = user.token;
|
||||||
if (token.length > 0) {
|
if (token.length > 0) {
|
||||||
[[KBAuthManager shared] saveAccessToken:token
|
[[KBAuthManager shared] saveAccessToken:token
|
||||||
refreshToken:user.refreshToken
|
refreshToken:nil
|
||||||
expiryDate:user.expiryDate
|
expiryDate:nil
|
||||||
userIdentifier:user.appleUserId ?: user.userId];
|
userIdentifier:nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再缓存用户信息,供 App/键盘使用
|
// 再缓存用户信息,供 App/键盘使用
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
|||||||
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
_enabled = NO; // 键盘扩展默认无网络能力,需外部显式开启
|
||||||
_timeout = 10.0;
|
_timeout = 10.0;
|
||||||
// 默认请求头:Accept 任意类型 + 使用项目多语言管理器设置 Accept-Language
|
// 默认请求头:Accept 任意类型 + 使用项目多语言管理器设置 Accept-Language
|
||||||
NSString *lang = [KBLocalizationManager shared].currentLanguageCode ?: @"en";
|
NSString *lang = [KBLocalizationManager shared].currentLanguageCode ?: KBLanguageCodeEnglish;
|
||||||
_defaultHeaders = @{
|
_defaultHeaders = @{
|
||||||
@"Accept": @"*/*",
|
@"Accept": @"*/*",
|
||||||
@"Accept-Language": lang
|
@"Accept-Language": lang
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
|
|
||||||
/// 三方
|
/// 三方
|
||||||
#import <Masonry/Masonry.h>
|
#import <Masonry/Masonry.h>
|
||||||
|
#import <MJExtension/MJExtension.h>
|
||||||
|
|
||||||
// 公共配置
|
// 公共配置
|
||||||
#import "KBConfig.h"
|
#import "KBConfig.h"
|
||||||
#import "KBAPI.h" // 接口路径宏(统一管理)
|
#import "KBAPI.h" // 接口路径宏(统一管理)
|
||||||
|
|||||||
Reference in New Issue
Block a user