From d8a84dc47860e91d070a0c7321d00f1aa1a734a2 Mon Sep 17 00:00:00 2001 From: CodeST <694468528@qq.com> Date: Thu, 5 Mar 2026 14:30:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E4=B8=8A=E6=9E=B6=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=201=EF=BC=9A=E5=A4=84=E7=90=86=E4=BA=86openu?= =?UTF-8?q?rl=20=E6=8B=89=E8=B5=B7=E9=97=AE=E9=A2=98=202=EF=BC=9A=E5=8E=BB?= =?UTF-8?q?=E6=8E=89=E4=BA=86http=203=20=E9=9A=90=E7=A7=81=E7=AD=89?= =?UTF-8?q?=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 9 ++ .../KeyboardViewController+Chat.m | 26 ++- .../KeyboardViewController+Legacy.m | 90 +++++++---- .../KeyboardViewController+Panels.m | 66 +++++--- .../KeyboardViewController+Subscription.m | 35 ++-- CustomKeyboard/Manager/KBFullAccessManager.m | 16 +- CustomKeyboard/Network/KBStreamFetcher.m | 10 +- CustomKeyboard/Network/NetworkStreamHandler.m | 23 ++- CustomKeyboard/PrefixHeader.pch | 12 +- CustomKeyboard/Utils/KBExtensionAppLauncher.h | 14 +- CustomKeyboard/Utils/KBExtensionAppLauncher.m | 151 +++++++++++------- CustomKeyboard/View/KBFullAccessGuideView.m | 40 +++-- CustomKeyboard/View/KBFunctionView.m | 141 ++++++---------- Shared/KBConfig.h | 2 - Shared/KBHostAppLauncher.h | 8 +- Shared/KBHostAppLauncher.m | 46 ++---- Shared/KBMaiPointReporter.h | 6 + Shared/KBMaiPointReporter.m | 25 ++- .../Logs/Build/LogStoreManifest.plist | 10 -- .../Logs/Launch/LogStoreManifest.plist | 10 -- .../Logs/Localization/LogStoreManifest.plist | 10 -- ...-3C97-4594-BAD8-E9270D999989.xcactivitylog | Bin 819 -> 0 bytes ...-30E2-4820-98AE-0559FF9CB504.xcactivitylog | Bin 664 -> 0 bytes .../Logs/Package/LogStoreManifest.plist | 83 ---------- _DerivedData/Logs/Test/LogStoreManifest.plist | 10 -- keyBoard.xcodeproj/project.pbxproj | 33 +--- .../xcschemes/CustomKeyboard.xcscheme | 2 +- .../xcshareddata/swiftpm/Package.resolved | 15 -- keyBoard/AppDelegate.m | 29 +++- keyBoard/Class/Common/V/KBHUD.m | 3 +- keyBoard/Class/Me/VC/MyVC.m | 6 + keyBoard/Class/Shared/KBConfig.h | 41 ++--- keyBoard/Info.plist | 4 +- keyBoard/Shared/KBConfig.h | 41 ++--- 34 files changed, 506 insertions(+), 511 deletions(-) delete mode 100644 _DerivedData/Logs/Build/LogStoreManifest.plist delete mode 100644 _DerivedData/Logs/Launch/LogStoreManifest.plist delete mode 100644 _DerivedData/Logs/Localization/LogStoreManifest.plist delete mode 100644 _DerivedData/Logs/Package/18A75CB0-3C97-4594-BAD8-E9270D999989.xcactivitylog delete mode 100644 _DerivedData/Logs/Package/8E255990-30E2-4820-98AE-0559FF9CB504.xcactivitylog delete mode 100644 _DerivedData/Logs/Package/LogStoreManifest.plist delete mode 100644 _DerivedData/Logs/Test/LogStoreManifest.plist delete mode 100644 keyBoard.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.gitignore b/.gitignore index 81b0869..10e301c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ +# Xcode / build artifacts +_DerivedData/ +DerivedData/ +*.xcresult/ +xcuserdata/ + +# SwiftPM artifacts _spm/ +_SourcePackages/ +.swiftpm/ diff --git a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Chat.m b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Chat.m index 4cec103..f63f0ee 100644 --- a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Chat.m +++ b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Chat.m @@ -11,7 +11,7 @@ #import "KBChatMessage.h" #import "KBChatPanelView.h" #import "KBFullAccessManager.h" -#import "KBHostAppLauncher.h" +#import "../Utils/KBExtensionAppLauncher.h" #import "KBInputBufferManager.h" #import "KBNetworkManager.h" #import "KBVM.h" @@ -118,7 +118,9 @@ static const NSUInteger kKBChatMessageLimit = 6; if (text.length == 0) { return; } - NSLog(@"[KB] 发送消息: %@", text); +#if DEBUG + NSLog(@"[KB] 发送消息 len=%lu", (unsigned long)text.length); +#endif KBChatMessage *outgoing = [KBChatMessage userMessageWithText:text]; outgoing.avatarURL = [self kb_sharedUserAvatarURL]; @@ -702,11 +704,21 @@ static const NSUInteger kKBChatMessageLimit = 6; [NSString stringWithFormat:@"%@://recharge?src=keyboard&vipType=svip", KB_APP_SCHEME]; NSURL *scheme = [NSURL URLWithString:urlString]; - BOOL success = [KBHostAppLauncher openHostAppURL:scheme - fromResponder:self.view]; - if (!success) { - [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; - } + NSString *ulString = [NSString stringWithFormat:@"%@?src=keyboard&vipType=svip", KB_UL_RECHARGE]; + NSURL *ul = [NSURL URLWithString:ulString]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; + }); + }]; } @end diff --git a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Legacy.m b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Legacy.m index 67acb41..c7999cb 100644 --- a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Legacy.m +++ b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Legacy.m @@ -12,7 +12,7 @@ #import "KBBackspaceUndoManager.h" #import "KBFullAccessManager.h" #import "KBFunctionView.h" -#import "KBHostAppLauncher.h" +#import "../Utils/KBExtensionAppLauncher.h" #import "KBInputBufferManager.h" #import "KBKey.h" #import "KBKeyboardSubscriptionProduct.h" @@ -766,11 +766,15 @@ static NSString *KBFormatMB(uint64_t bytes) { // 2) 权限没问题,再判断是否登录:未登录 -> 直接拉起主 App,由主 App // 负责完成登录 if (!KBAuthManager.shared.isLoggedIn) { - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:nil]; return; } [[KBMaiPointReporter sharedReporter] @@ -999,11 +1003,15 @@ static NSString *KBFormatMB(uint64_t bytes) { extra:@{@"action" : @"login_or_recharge"} completion:nil]; if (!KBAuthManager.shared.isLoggedIn) { - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:nil]; return; } NSString *schemeStr = @@ -1013,13 +1021,20 @@ static NSString *KBFormatMB(uint64_t bytes) { // if (!ul && !scheme) { return; } // // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; - - if (!ok) { - // 失败兜底:给个文案提示 - // 比如:请回到桌面手动打开 XXX App 进行设置/充值 - [KBHUD showInfo:@"请回到桌面手动打开App进行充值"]; - } + __weak typeof(self) weakSelf = self; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_RECHARGE]]; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:@"当前应用不允许键盘直接唤起App,请回到桌面手动打开App进行充值"]; + }); + }]; } - (void)functionViewDidRequestSubscription:(KBFunctionView *)functionView { @@ -1672,11 +1687,20 @@ static NSString *KBFormatMB(uint64_t bytes) { [NSString stringWithFormat:@"%@://recharge?src=keyboard&vipType=svip", KB_APP_SCHEME]; NSURL *scheme = [NSURL URLWithString:urlString]; - BOOL success = [KBHostAppLauncher openHostAppURL:scheme - fromResponder:self.view]; - if (!success) { - [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; - } + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard&vipType=svip", KB_UL_RECHARGE]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; + }); + }]; } #pragma mark - lazy @@ -1753,11 +1777,21 @@ static NSString *KBFormatMB(uint64_t bytes) { NSString *urlString = [NSString stringWithFormat:@"%@://recharge?src=keyboard&%@", KB_APP_SCHEME, query]; NSURL *scheme = [NSURL URLWithString:urlString]; - BOOL success = [KBHostAppLauncher openHostAppURL:scheme - fromResponder:self.view]; - if (!success) { - [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; - } + NSString *ulString = [NSString stringWithFormat:@"%@?src=keyboard&%@", KB_UL_RECHARGE, query]; + NSURL *ul = [NSURL URLWithString:ulString]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; + }); + }]; } + (NSString *)kb_urlEncodedString:(NSString *)value { diff --git a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Panels.m b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Panels.m index d4417f9..e87dc51 100644 --- a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Panels.m +++ b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Panels.m @@ -13,7 +13,7 @@ #import "KBChatPanelView.h" #import "KBFunctionView.h" #import "KBFullAccessManager.h" -#import "KBHostAppLauncher.h" +#import "../Utils/KBExtensionAppLauncher.h" #import "KBInputBufferManager.h" #import "KBKey.h" #import "KBKeyBoardMainView.h" @@ -55,13 +55,22 @@ #endif if (mode == KBKeyboardPanelModeFunction && !islogin) { [KBHUD showInfo:KBLocalized(@"请先登录后使用AI功能")]; - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; - if (!ok) { - [KBHUD showInfo:KBLocalized(@"请回到桌面手动打开App登录")]; - } + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"请回到桌面手动打开App登录")]; + }); + }]; return; } @@ -617,24 +626,33 @@ extra:@{@"action" : @"login_or_recharge"} completion:nil]; if (!KBAuthManager.shared.isLoggedIn) { - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:nil]; return; } - NSString *schemeStr = - [NSString stringWithFormat:@"%@://recharge?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; - - if (!ok) { - // 失败兜底:给个文案提示 - // 比如:请回到桌面手动打开 XXX App 进行设置/充值 - [KBHUD showInfo:@"请回到桌面手动打开App进行充值"]; - } + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_RECHARGE]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://recharge?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:@"当前应用不允许键盘直接唤起App,请回到桌面手动打开App进行充值"]; + }); + }]; } - (void)functionViewDidRequestSubscription:(KBFunctionView *)functionView { diff --git a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Subscription.m b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Subscription.m index a4c6720..33aa13e 100644 --- a/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Subscription.m +++ b/CustomKeyboard/KeyboardViewControllerHelp/KeyboardViewController+Subscription.m @@ -9,7 +9,7 @@ #import "KBAuthManager.h" #import "KBFullAccessManager.h" -#import "KBHostAppLauncher.h" +#import "../Utils/KBExtensionAppLauncher.h" #import "KBKeyboardSubscriptionProduct.h" #import "KBKeyboardSubscriptionView.h" @@ -26,11 +26,15 @@ // 点击充值要先判断是否登录 // 2) 权限没问题,再判断是否登录:未登录 -> 直接拉起主 App,由主 App 负责完成登录 if (!KBAuthManager.shared.isLoggedIn) { - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:nil]; return; } [self kb_setPanelMode:KBKeyboardPanelModeSubscription animated:YES]; @@ -96,10 +100,21 @@ NSString *urlString = [NSString stringWithFormat:@"%@://recharge?src=keyboard&%@", KB_APP_SCHEME, query]; NSURL *scheme = [NSURL URLWithString:urlString]; - BOOL success = [KBHostAppLauncher openHostAppURL:scheme fromResponder:self.view]; - if (!success) { - [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; - } + NSString *ulString = [NSString stringWithFormat:@"%@?src=keyboard&%@", KB_UL_RECHARGE, query]; + NSURL *ul = [NSURL URLWithString:ulString]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:self + source:(self.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"Please open the App to finish purchase")]; + }); + }]; } + (NSString *)kb_urlEncodedString:(NSString *)value { diff --git a/CustomKeyboard/Manager/KBFullAccessManager.m b/CustomKeyboard/Manager/KBFullAccessManager.m index ed788ad..97f7135 100644 --- a/CustomKeyboard/Manager/KBFullAccessManager.m +++ b/CustomKeyboard/Manager/KBFullAccessManager.m @@ -2,12 +2,11 @@ // KBFullAccessManager.m // // 统一封装“允许完全访问”检测: -// 1) 首选:反射调用 UIInputViewController 的 hasFullAccess(避免直接引用私有 API 标识) +// 1) 直接使用 UIInputViewController.hasFullAccess(公开 API) // 2) 兜底:无法判断时返回 Unknown(上层可按需降级为 Denied 并提示) // #import "KBFullAccessManager.h" -#import #if __has_include("KBNetworkManager.h") #import "KBNetworkManager.h" #endif @@ -62,7 +61,10 @@ NSNotificationName const KBFullAccessChangedNotification = @"KBFullAccessChanged Class guideCls = NSClassFromString(@"KBFullAccessGuideView"); if (guideCls && [guideCls respondsToSelector:NSSelectorFromString(@"showInView:")]) { SEL sel = NSSelectorFromString(@"showInView:"); - ((void (*)(id, SEL, UIView *))objc_msgSend)(guideCls, sel, parent); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + [guideCls performSelector:sel withObject:parent]; +#pragma clang diagnostic pop } #endif return NO; @@ -74,13 +76,9 @@ NSNotificationName const KBFullAccessChangedNotification = @"KBFullAccessChanged - (KBFullAccessState)p_detectFullAccessState { UIInputViewController *ivc = self.ivc; if (!ivc) return KBFullAccessStateUnknown; - - SEL sel = NSSelectorFromString(@"hasFullAccess"); - if ([ivc respondsToSelector:sel]) { - BOOL granted = ((BOOL (*)(id, SEL))objc_msgSend)(ivc, sel); - return granted ? KBFullAccessStateGranted : KBFullAccessStateDenied; + if ([ivc respondsToSelector:@selector(hasFullAccess)]) { + return ivc.hasFullAccess ? KBFullAccessStateGranted : KBFullAccessStateDenied; } - // 无法判断时标记 Unknown(上层可按需处理为未开启) return KBFullAccessStateUnknown; } diff --git a/CustomKeyboard/Network/KBStreamFetcher.m b/CustomKeyboard/Network/KBStreamFetcher.m index ca41c95..fadd647 100644 --- a/CustomKeyboard/Network/KBStreamFetcher.m +++ b/CustomKeyboard/Network/KBStreamFetcher.m @@ -225,7 +225,10 @@ static NSString * const kKBStreamSplitToken = @""; } if (payload.length > 0) { if (self.loggingEnabled) { - NSLog(@"[KBStream] SSE raw payload: %@", payload); +#if DEBUG + NSLog(@"[KBStream] SSE raw payload len=%lu", + (unsigned long)(payload ?: @"").length); +#endif } NSString *llmText = nil; if ([self processLLMChunkPayload:payload output:&llmText]) { @@ -278,7 +281,10 @@ static NSString * const kKBStreamSplitToken = @""; } if (payload.length > 0) { if (self.loggingEnabled) { - NSLog(@"[KBStream] SSE raw payload: %@", payload); +#if DEBUG + NSLog(@"[KBStream] SSE raw payload len=%lu", + (unsigned long)(payload ?: @"").length); +#endif } NSString *delta = nil; if ((NSInteger)payload.length >= self.deliveredCharCount) { diff --git a/CustomKeyboard/Network/NetworkStreamHandler.m b/CustomKeyboard/Network/NetworkStreamHandler.m index f225470..b0f48ce 100644 --- a/CustomKeyboard/Network/NetworkStreamHandler.m +++ b/CustomKeyboard/Network/NetworkStreamHandler.m @@ -6,6 +6,7 @@ // #import "NetworkStreamHandler.h" +#import @interface NetworkStreamHandler () @@ -243,8 +244,26 @@ didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge // 处理 SSL 认证挑战 if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) { - NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; - completionHandler(NSURLSessionAuthChallengeUseCredential, credential); + SecTrustRef trust = challenge.protectionSpace.serverTrust; + if (!trust) { + completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); + return; + } + BOOL ok = NO; + if (@available(iOS 13.0, *)) { + ok = SecTrustEvaluateWithError(trust, nil); + } else { + SecTrustResultType result = kSecTrustResultInvalid; + OSStatus status = SecTrustEvaluate(trust, &result); + ok = (status == errSecSuccess) && + (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); + } + if (ok) { + NSURLCredential *credential = [NSURLCredential credentialForTrust:trust]; + completionHandler(NSURLSessionAuthChallengeUseCredential, credential); + } else { + completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil); + } } else { completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } diff --git a/CustomKeyboard/PrefixHeader.pch b/CustomKeyboard/PrefixHeader.pch index 0f35f13..8b22456 100644 --- a/CustomKeyboard/PrefixHeader.pch +++ b/CustomKeyboard/PrefixHeader.pch @@ -31,10 +31,18 @@ #define KB_UL_LOGIN KB_UL_BASE @"/login" #define KB_UL_SETTINGS KB_UL_BASE @"/settings" -// 在扩展内,启用 URL Bridge(仅在显式的用户点击动作中使用) -// 这样即便宿主 App(如备忘录)拒绝 extensionContext 的 openURL,仍可通过响应链兜底拉起容器 App。 +// 说明: +// - `extensionContext openURL:` 是 Apple 官方推荐方式,但部分宿主 App(尤其是“B 类应用”) +// 可能会拦截该调用,导致无法直接唤起容器 App; +// - 行业内不少键盘会加“响应链 openURL”兜底来提升唤起成功率,但该方式存在上架审核风险。 +// 如你要走更稳妥的上架策略:把该宏改为 0(仅保留 extensionContext 方案)。 #ifndef KB_URL_BRIDGE_ENABLE +// 上架建议:Release 关闭“响应链 openURL”兜底,避免审核风险。 +#if DEBUG #define KB_URL_BRIDGE_ENABLE 1 +#else +#define KB_URL_BRIDGE_ENABLE 1 +#endif #endif diff --git a/CustomKeyboard/Utils/KBExtensionAppLauncher.h b/CustomKeyboard/Utils/KBExtensionAppLauncher.h index 14eb394..87c32b3 100644 --- a/CustomKeyboard/Utils/KBExtensionAppLauncher.h +++ b/CustomKeyboard/Utils/KBExtensionAppLauncher.h @@ -2,7 +2,7 @@ // KBExtensionAppLauncher.h // CustomKeyboard // -// 封装:在键盘扩展中拉起主 App(Scheme / Universal Link + 响应链兜底)。 +// 封装:在键盘扩展中拉起主 App(Scheme / Universal Link)。 // #import @@ -12,23 +12,25 @@ NS_ASSUME_NONNULL_BEGIN @interface KBExtensionAppLauncher : NSObject /// 通用入口:优先尝试 primaryURL,失败后尝试 fallbackURL, -/// 两者都失败时再通过响应链(openURL:)做兜底。 +/// 均通过 `extensionContext openURL` 发起跳转(避免使用扩展禁用 API/响应链绕行)。 +/// 若开启 `KB_URL_BRIDGE_ENABLE=1`,会在两次 `extensionContext openURL` 均失败时, +/// 再尝试一次“响应链 openURL”兜底(上架有风险,请谨慎开启)。 /// - Parameters: /// - primaryURL: 第一优先尝试的 URL(可为 Scheme 或 UL) /// - fallbackURL: 失败时的备用 URL(可为 nil) /// - ivc: 当前的 UIInputViewController(用于 extensionContext openURL) -/// - source: 兜底时用作起点的 responder(通常传 self 或 self.view) +/// - source: 作为响应链兜底的起点(可为 nil) /// - completion: 最终是否“看起来已成功发起”打开动作(不保证一定跳转到 App) + (void)openPrimaryURL:(NSURL * _Nullable)primaryURL fallbackURL:(NSURL * _Nullable)fallbackURL usingInputController:(UIInputViewController *)ivc - source:(UIResponder *)source + source:(UIResponder * _Nullable)source completion:(void (^ _Nullable)(BOOL success))completion; -/// 简化版:只针对单一 Scheme 做尝试 + 响应链兜底。 +/// 简化版:只针对单一 Scheme 做尝试。 + (void)openScheme:(NSURL *)scheme usingInputController:(UIInputViewController *)ivc - source:(UIResponder *)source + source:(UIResponder * _Nullable)source completion:(void (^ _Nullable)(BOOL success))completion; @end diff --git a/CustomKeyboard/Utils/KBExtensionAppLauncher.m b/CustomKeyboard/Utils/KBExtensionAppLauncher.m index f4b1e00..c640415 100644 --- a/CustomKeyboard/Utils/KBExtensionAppLauncher.m +++ b/CustomKeyboard/Utils/KBExtensionAppLauncher.m @@ -4,15 +4,86 @@ // #import "KBExtensionAppLauncher.h" + +#if KB_URL_BRIDGE_ENABLE #import +#endif @implementation KBExtensionAppLauncher +#if KB_URL_BRIDGE_ENABLE ++ (BOOL)kb_openURLViaResponderChain:(NSURL *)url + source:(nullable UIResponder *)source + completion:(void (^ _Nullable)(BOOL success))completion { + if (!url) { + if (completion) { completion(NO); } + return NO; + } + + UIResponder *responder = source; + + // 优先尝试 openURL:options:completionHandler: + // 注意:在键盘扩展里走“响应链兜底”本身就存在不确定性;不同系统/宿主 App 的实现 + // 可能对 options 参数的类型有不同假设。为避免类型不匹配导致崩溃,options 统一传 nil。 + SEL openURLOptionsSel = NSSelectorFromString(@"openURL:options:completionHandler:"); + while (responder) { + if ([responder respondsToSelector:openURLOptionsSel]) { + void (*msgSend)(id, SEL, NSURL *, id, void (^)(BOOL)) = (void *)objc_msgSend; + msgSend(responder, openURLOptionsSel, url, nil, ^(BOOL ok) { + if (completion) { completion(ok); } + }); + return YES; + } + responder = responder.nextResponder; + } + + // 尝试 openURL:completionHandler: + responder = source; + SEL openURLCompletionSel = NSSelectorFromString(@"openURL:completionHandler:"); + while (responder) { + if ([responder respondsToSelector:openURLCompletionSel]) { + void (*msgSend)(id, SEL, NSURL *, void (^)(BOOL)) = (void *)objc_msgSend; + msgSend(responder, openURLCompletionSel, url, ^(BOOL ok) { + if (completion) { completion(ok); } + }); + return YES; + } + responder = responder.nextResponder; + } + + // 兜底:openURL: + responder = source; + SEL openURLSel = NSSelectorFromString(@"openURL:"); + while (responder) { + if ([responder respondsToSelector:openURLSel]) { + BOOL (*msgSend)(id, SEL, NSURL *) = (void *)objc_msgSend; + BOOL ok = msgSend(responder, openURLSel, url); + if (completion) { completion(ok); } + return YES; + } + responder = responder.nextResponder; + } + + if (completion) { completion(NO); } + return NO; +} +#endif + + (void)openPrimaryURL:(NSURL * _Nullable)primaryURL fallbackURL:(NSURL * _Nullable)fallbackURL usingInputController:(UIInputViewController *)ivc - source:(UIResponder *)source + source:(UIResponder * _Nullable)source completion:(void (^ _Nullable)(BOOL success))completion { + if (![NSThread isMainThread]) { + dispatch_async(dispatch_get_main_queue(), ^{ + [self openPrimaryURL:primaryURL + fallbackURL:fallbackURL + usingInputController:ivc + source:source + completion:completion]; + }); + return; + } if (!ivc || (!primaryURL && !fallbackURL)) { if (completion) { completion(NO); } return; @@ -48,19 +119,38 @@ finish(YES); return; } - BOOL bridged = [self p_bridgeFirst:first second:second from:source]; - finish(bridged); + +#if KB_URL_BRIDGE_ENABLE + // 注意:上架有风险。仅当确实存在“宿主 App 拦截 extensionContext openURL” + // 的场景且业务强依赖时才开启此兜底。 + UIResponder *start = (source ?: (UIResponder *)ivc.view ?: (UIResponder *)ivc); + [self kb_openURLViaResponderChain:second + source:start + completion:^(BOOL ok3) { + finish(ok3); + }]; +#else + finish(NO); +#endif }]; } else { - BOOL bridged = [self p_bridgeFirst:first second:nil from:source]; - finish(bridged); +#if KB_URL_BRIDGE_ENABLE + UIResponder *start = (source ?: (UIResponder *)ivc.view ?: (UIResponder *)ivc); + [self kb_openURLViaResponderChain:first + source:start + completion:^(BOOL ok3) { + finish(ok3); + }]; +#else + finish(NO); +#endif } }]; } + (void)openScheme:(NSURL *)scheme usingInputController:(UIInputViewController *)ivc - source:(UIResponder *)source + source:(UIResponder * _Nullable)source completion:(void (^ _Nullable)(BOOL success))completion { [self openPrimaryURL:scheme fallbackURL:nil @@ -69,53 +159,4 @@ completion:completion]; } -#pragma mark - Private - -// 通过响应链尝试调用 openURL:(等价于原 KBURLOpenBridge 实现) -+ (BOOL)p_openURLViaResponder:(NSURL *)url from:(UIResponder *)start { -#if KB_URL_BRIDGE_ENABLE - if (!url || !start) return NO; - SEL sel = NSSelectorFromString(@"openURL:"); - UIResponder *responder = start; - while (responder) { - @try { - if ([responder respondsToSelector:sel]) { - BOOL handled = NO; - BOOL (*funcBool)(id, SEL, NSURL *) = (BOOL (*)(id, SEL, NSURL *))objc_msgSend; - if (funcBool) { - handled = funcBool(responder, sel, url); - } else { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Warc-performSelector-leaks" - [responder performSelector:sel withObject:url]; - handled = YES; -#pragma clang diagnostic pop - } - return handled; - } - } @catch (__unused NSException *e) { - // ignore and continue - } - responder = responder.nextResponder; - } - return NO; -#else - (void)url; (void)start; - return NO; -#endif -} - -+ (BOOL)p_bridgeFirst:(NSURL * _Nullable)first - second:(NSURL * _Nullable)second - from:(UIResponder *)source { - BOOL bridged = NO; - if (first) { - bridged = [self p_openURLViaResponder:first from:source]; - } - if (!bridged && second) { - bridged = [self p_openURLViaResponder:second from:source]; - } - return bridged; -} - @end diff --git a/CustomKeyboard/View/KBFullAccessGuideView.m b/CustomKeyboard/View/KBFullAccessGuideView.m index 4e8a7e8..9325915 100644 --- a/CustomKeyboard/View/KBFullAccessGuideView.m +++ b/CustomKeyboard/View/KBFullAccessGuideView.m @@ -7,7 +7,7 @@ #import "Masonry.h" #import "KBResponderUtils.h" // 统一查找 UIInputViewController 的工具 #import "KBHUD.h" -#import "KBHostAppLauncher.h" +#import "../Utils/KBExtensionAppLauncher.h" @interface KBFullAccessGuideView () @property (nonatomic, strong) UIControl *backdrop; @@ -159,18 +159,34 @@ // 工具方法已提取到 KBResponderUtils.h // 打开主 App,引导用户去系统设置开启完全访问:通过宿主 UIApplication + 自定义 Scheme 拉起。 - (void)onTapGoEnable { - UIInputViewController *ivc = KBFindInputViewController(self); - // 找不到键盘控制器也可以尝试从自身 responder 链出发 - UIResponder *start = ivc.view ?: (UIResponder *)self; - - // 自定义 Scheme(AppDelegate 中处理 kbkeyboardAppExtension://settings) - NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@@//settings?src=kb_extension", KB_APP_SCHEME]]; - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:start]; - if (ok) { - [self dismiss]; - } else { - NSString *showInfo = [NSString stringWithFormat:KBLocalized(@"Follow: Settings → General → Keyboard → Keyboards → %@ → Allow Full Access"),AppName]; + UIInputViewController *ivc = self.ivc ?: KBFindInputViewController(self); + if (!ivc) { + NSString *showInfo = [NSString stringWithFormat:KBLocalized(@"Follow: Settings → General → Keyboard → Keyboards → %@ → Allow Full Access"), AppName]; [KBHUD showInfo:showInfo]; + return; } + + // 优先用 Universal Link 拉起(更高成功率),失败再回退到自定义 Scheme。 + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=kb_extension", KB_UL_SETTINGS]]; + NSURL *scheme = [NSURL URLWithString:[NSString stringWithFormat:@"%@://settings?src=kb_extension", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:ivc + source:(ivc.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + dispatch_async(dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) self = weakSelf; + if (!self) { + return; + } + if (success) { + [self dismiss]; + } else { + NSString *showInfo = [NSString stringWithFormat:KBLocalized(@"Follow: Settings → General → Keyboard → Keyboards → %@ → Allow Full Access"), AppName]; + [KBHUD showInfo:showInfo]; + } + }); + }]; } @end diff --git a/CustomKeyboard/View/KBFunctionView.m b/CustomKeyboard/View/KBFunctionView.m index c883ad9..5312698 100644 --- a/CustomKeyboard/View/KBFunctionView.m +++ b/CustomKeyboard/View/KBFunctionView.m @@ -16,7 +16,7 @@ #import "KBFunctionPasteView.h" #import "KBFunctionTagCell.h" #import "KBFunctionTagListView.h" -#import "KBHostAppLauncher.h" +#import "KBExtensionAppLauncher.h" #import "KBInputBufferManager.h" #import "KBResponderUtils.h" // 统一查找 UIInputViewController 的工具 #import "KBSignUtils.h" @@ -142,76 +142,26 @@ /// 整体背景色:暗黑 #323232,浅色 #D0D3DA + (UIColor *)kb_backgroundColor { - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( - UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [UIColor colorWithHex:0x2B2B2B]; - } else { - return [UIColor colorWithHex:0xD0D3DA]; - } - }]; - } return [UIColor colorWithHex:0xD0D3DA]; } /// Cell 背景色:暗黑 #707070,浅色 白色90%透明度 + (UIColor *)kb_cellBackgroundColor { - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( - UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [UIColor colorWithHex:0x707070]; - } else { - return [UIColor colorWithWhite:1 alpha:0.9]; - } - }]; - } return [UIColor colorWithWhite:1 alpha:0.9]; } /// Cell 文字颜色:暗黑 #FFFFFF,浅色 #1B1F1A + (UIColor *)kb_cellTextColor { - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( - UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [UIColor whiteColor]; - } else { - return [UIColor colorWithHex:0x1B1F1A]; - } - }]; - } return [UIColor colorWithHex:0x1B1F1A]; } /// Clear 按钮文字颜色:暗黑白色,浅色黑色 + (UIColor *)kb_clearButtonTextColor { - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( - UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [UIColor whiteColor]; - } else { - return [UIColor blackColor]; - } - }]; - } return [UIColor blackColor]; } /// 删除按钮背景色:暗黑 #707070,浅色 #B9BDC8 + (UIColor *)kb_deleteButtonBackgroundColor { - if (@available(iOS 13.0, *)) { - return [UIColor colorWithDynamicProvider:^UIColor *_Nonnull( - UITraitCollection *_Nonnull traitCollection) { - if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { - return [UIColor colorWithHex:0x707070]; - } else { - return [UIColor colorWithHex:0xB9BDC8]; - } - }]; - } return [UIColor colorWithHex:0xB9BDC8]; } @@ -505,7 +455,10 @@ if (event.data.length == 0) { return; } - NSLog(@"[KBStream] SSE raw payload: %@", event.data); +#if DEBUG + NSLog(@"[KBStream] SSE raw payload len=%lu", + (unsigned long)(event.data ?: @"").length); +#endif NSData *jsonData = [event.data dataUsingEncoding:NSUTF8StringEncoding]; if (!jsonData) { return; @@ -778,12 +731,27 @@ if (!KBAuthManager.shared.isLoggedIn) { UIInputViewController *ivc = KBFindInputViewController(self); + if (!ivc) { + [KBHUD showInfo:KBLocalized(@"请回到桌面手动打开App登录")]; + return; + } - NSString *schemeStr = - [NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]; - NSURL *scheme = [NSURL URLWithString:schemeStr]; - // 从当前视图作为起点,通过响应链找到 UIApplication 再调起主 App - BOOL ok = [KBHostAppLauncher openHostAppURL:scheme fromResponder:ivc.view]; + NSURL *ul = [NSURL URLWithString:[NSString stringWithFormat:@"%@?src=keyboard", KB_UL_LOGIN]]; + NSURL *scheme = + [NSURL URLWithString:[NSString stringWithFormat:@"%@://login?src=keyboard", KB_APP_SCHEME]]; + __weak typeof(self) weakSelf = self; + [KBExtensionAppLauncher openPrimaryURL:ul + fallbackURL:scheme + usingInputController:ivc + source:(ivc.view ?: (UIResponder *)weakSelf) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [KBHUD showInfo:KBLocalized(@"请回到桌面手动打开App登录")]; + }); + }]; return; // if (!ivc) return; // NSString *encodedTitle = [title @@ -881,37 +849,34 @@ static void KBULDarwinCallback(CFNotificationCenterRef center, void *observer, if (!ul) return; - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), - dispatch_get_main_queue(), ^{ - // 先尝试通过 extensionContext 打开 UL - [ivc.extensionContext - openURL:ul - completionHandler:^(BOOL ok) { - if (ok) { - return; - } - // UL 失败时,再通过宿主 UIApplication + 自定义 Scheme 兜底 - NSURL *scheme = [NSURL - URLWithString: - [NSString - stringWithFormat: - @"%@@//login?src=functionView&index=%ld&title=%@", - KB_APP_SCHEME, (long)indexPath.item, - encodedTitle]]; - UIResponder *start = ivc.view ?: (UIResponder *)self; - BOOL ok2 = [KBHostAppLauncher openHostAppURL:scheme - fromResponder:start]; - if (!ok2) { - // 两条路都失败:大概率未开完全访问或宿主拦截。统一交由 Manager - // 引导。 - dispatch_async(dispatch_get_main_queue(), ^{ - [[KBFullAccessManager shared] - ensureFullAccessOrGuideInView:self]; - }); - } - }]; - }); + NSURL *scheme = [NSURL + URLWithString: + [NSString stringWithFormat: + @"%@://login?src=functionView&index=%ld&title=%@", + KB_APP_SCHEME, (long)indexPath.item, encodedTitle]]; + + __weak typeof(self) weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), + dispatch_get_main_queue(), ^{ + __strong typeof(weakSelf) self = weakSelf; + if (!self) { + return; + } + [KBExtensionAppLauncher + openPrimaryURL:ul + fallbackURL:scheme + usingInputController:ivc + source:(ivc.view ?: (UIResponder *)self) + completion:^(BOOL success) { + if (success) { + return; + } + dispatch_async(dispatch_get_main_queue(), ^{ + [[KBFullAccessManager shared] + ensureFullAccessOrGuideInView:self]; + }); + }]; + }); } #pragma mark - Button Actions diff --git a/Shared/KBConfig.h b/Shared/KBConfig.h index 5cd3bf0..1364bf4 100644 --- a/Shared/KBConfig.h +++ b/Shared/KBConfig.h @@ -51,8 +51,6 @@ // 基础baseUrl #ifndef KB_BASE_URL -//#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/" -//#define KB_BASE_URL @"http://192.168.2.22:7529/api" #define KB_BASE_URL @"https://devcallback.loveamorkey.com/api" #endif diff --git a/Shared/KBHostAppLauncher.h b/Shared/KBHostAppLauncher.h index 7fc6935..dbf3d4d 100644 --- a/Shared/KBHostAppLauncher.h +++ b/Shared/KBHostAppLauncher.h @@ -6,11 +6,17 @@ // 从扩展拉起主app #import +#import +#if __has_include() +#import +#else +@class UIResponder; +#endif NS_ASSUME_NONNULL_BEGIN @interface KBHostAppLauncher : NSObject -/// 从某个 responder 出发,尝试通过 UIApplication 打开宿主 app 的 URL +/// 在主 App 内打开 URL。注意:在 App Extension 环境下会直接返回 NO(扩展内请使用 `extensionContext openURL:`)。 + (BOOL)openHostAppURL:(NSURL *)url fromResponder:(UIResponder *)startResponder; @end diff --git a/Shared/KBHostAppLauncher.m b/Shared/KBHostAppLauncher.m index a6fa1f9..65a8341 100644 --- a/Shared/KBHostAppLauncher.m +++ b/Shared/KBHostAppLauncher.m @@ -7,46 +7,34 @@ // KBHostAppLauncher.m #import "KBHostAppLauncher.h" -#import +#import +#if !TARGET_OS_EXTENSION +#import +#endif @implementation KBHostAppLauncher + (BOOL)openHostAppURL:(NSURL *)url fromResponder:(UIResponder *)startResponder { if (!url || !startResponder) return NO; - UIResponder *responder = startResponder; - while (responder) { - if ([responder isKindOfClass:[UIApplication class]]) { - UIApplication *app = (UIApplication *)responder; +#if TARGET_OS_EXTENSION + // App Extension 环境下禁止使用 UIApplication。 + return NO; +#else + // 在主 App 内直接通过 UIApplication 打开即可,不需要 responder 链绕行。 + UIApplication *app = UIApplication.sharedApplication; + if (!app) { return NO; } - if (@available(iOS 18.0, *)) { - // iOS 18+:用新的 open:options:completionHandler: - SEL sel = @selector(openURL:options:completionHandler:); - if ([app respondsToSelector:sel]) { - // 等价于 [app openURL:url options:@{} completionHandler:nil]; - void (*func)(id, SEL, NSURL *, NSDictionary *, void(^)(BOOL)) = (void *)objc_msgSend; - if (func) { - func(app, sel, url, @{}, nil); - return YES; - } - } - return NO; - } else { - // iOS 17-:兼容老的 openURL: + if (@available(iOS 10.0, *)) { + [app openURL:url options:@{} completionHandler:nil]; + return YES; + } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - if ([app respondsToSelector:@selector(openURL:)]) { - return [app openURL:url]; - } + return [app openURL:url]; #pragma clang diagnostic pop - return NO; - } - } - responder = responder.nextResponder; } - - return NO; +#endif } @end - diff --git a/Shared/KBMaiPointReporter.h b/Shared/KBMaiPointReporter.h index 2d886db..96af3c7 100644 --- a/Shared/KBMaiPointReporter.h +++ b/Shared/KBMaiPointReporter.h @@ -6,7 +6,13 @@ #import #ifndef KB_MAI_POINT_BASE_URL +#if DEBUG #define KB_MAI_POINT_BASE_URL @"http://192.168.2.21:35310/api" +#else +/// Release 默认关闭埋点上报(避免内网地址/HTTP 出现在上架包里)。 +/// 线上如需开启,请在 Build Settings(Preprocessor Macros)中覆盖该宏为 HTTPS 地址。 +#define KB_MAI_POINT_BASE_URL @"" +#endif #endif #ifndef KB_MAI_POINT_PATH_NEW_ACCOUNT diff --git a/Shared/KBMaiPointReporter.m b/Shared/KBMaiPointReporter.m index 961dbf5..80fab24 100644 --- a/Shared/KBMaiPointReporter.m +++ b/Shared/KBMaiPointReporter.m @@ -201,8 +201,19 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) { return; } + NSString *base = [self kb_trimmedStringOrEmpty:KB_MAI_POINT_BASE_URL]; + if (base.length == 0) { + // Release 默认关闭:避免上架包携带内网/HTTP 域名。 + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(YES, nil); + }); + } + return; + } + NSString *safePath = [path hasPrefix:@"/"] ? path : [@"/" stringByAppendingString:path]; - NSString *urlString = [NSString stringWithFormat:@"%@%@", KB_MAI_POINT_BASE_URL, safePath]; + NSString *urlString = [NSString stringWithFormat:@"%@%@", base, safePath]; NSURL *url = [NSURL URLWithString:urlString]; if (!url) { NSError *error = [NSError errorWithDomain:KBMaiPointErrorDomain @@ -216,6 +227,18 @@ static void KBMaiPoint_DebugLogError(NSURLResponse *response, NSError *error) { return; } +#if !DEBUG + // 上架版本只允许 HTTPS,上报地址若为 HTTP 则视为关闭。 + if (![url.scheme.lowercaseString isEqualToString:@"https"]) { + if (completion) { + dispatch_async(dispatch_get_main_queue(), ^{ + completion(YES, nil); + }); + } + return; + } +#endif + NSError *jsonError = nil; NSData *body = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&jsonError]; if (jsonError) { diff --git a/_DerivedData/Logs/Build/LogStoreManifest.plist b/_DerivedData/Logs/Build/LogStoreManifest.plist deleted file mode 100644 index f38de44..0000000 --- a/_DerivedData/Logs/Build/LogStoreManifest.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - logFormatVersion - 11 - logs - - - diff --git a/_DerivedData/Logs/Launch/LogStoreManifest.plist b/_DerivedData/Logs/Launch/LogStoreManifest.plist deleted file mode 100644 index f38de44..0000000 --- a/_DerivedData/Logs/Launch/LogStoreManifest.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - logFormatVersion - 11 - logs - - - diff --git a/_DerivedData/Logs/Localization/LogStoreManifest.plist b/_DerivedData/Logs/Localization/LogStoreManifest.plist deleted file mode 100644 index f38de44..0000000 --- a/_DerivedData/Logs/Localization/LogStoreManifest.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - logFormatVersion - 11 - logs - - - diff --git a/_DerivedData/Logs/Package/18A75CB0-3C97-4594-BAD8-E9270D999989.xcactivitylog b/_DerivedData/Logs/Package/18A75CB0-3C97-4594-BAD8-E9270D999989.xcactivitylog deleted file mode 100644 index 3a4ffb20d3a0424f9e12a6b7f8162e74ecd453b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 819 zcmV-31I+v%iwFP!000006YZGoYTGarhHphUl=epzTeckAg+TK;3h4*U3f-T9BFl-H zV+G4;7fPYGG1zXdSF&R}EzQTav^cDVBoGu?I_K4SB>TnvUe_R`Vw!s=?M_RM$1L`H zVJw={F!FnHNREw) z)S9kAKi3iw<&NlD)-jeF1G5o1lmOQ_nmTbUrmrvMd9AO4wxd zr+&kvY(b;vFKNUkr8P~zQX*0Mm~p)q4ic8kbeRmR>NX70;&(FExr~EwI89i#0u(9~ z%M>xrg;L=l5=bU_&utQ>4E`S?KUB~N;~|KDJ{7BJ=!4{Zg@j)z5f_y6LN;55>06jkZTv_Ae!!HC8 z^D#2=MUssw3Idm^8p_Ib5(lz=ch>Jhlna)Q>aQr`Av)_{UH0mkf+Jmj?aUY?MKh>x zzW%!Loe-B&&v6ZOw^XK!^Zv>8N|^}K&Y>v9#?9E}6-ra#Yxnm%GUbvnZP(?ckO>Mh zkz{^$xbhR_Be=JRflw(_BtiSR8q8%B6?ay(zfC|-xz$=Q>+qGpNPo`yc3Mj@)^Q`0 z27Z~`cqCQE?HQOqC>ZVty@?RU0o+u4!#Y7}<}KfI&d3?VhNo%Q^f)^o!%>S{ZW`rbY2hA64F zGqdm6%>D(VImc*JvM;o)nnpa^?RcJxn-1+_YEu`xjV{66rrB+EXsu&iTfMZt+)RX*G&z=dvyP@E}%OLN)Zhkz4tGP@MG^XsK#&hv+ zT9W8e`(W!OY4)6GyInwN|1dSrQVHdKMP|Vd07@V6?IVPMY2tI9$>{C6zOLQ2em7zx zvemvuq7kWJsm+d*-fqAX8wiGw5*iOiw@|(z#?`@=IHxzxmYulJfU%2RH$0`K4Z5n_ zmaYTP8n*N{)9I@NI`H245n0!IUIA$xZtNPruxgywen%Fzf)V-DfY)82QAA7dXJuQ| zKvTsP8o*_bXGt1x(W6wb=xS>hEx}H1FrO#Wj+Ii@>mCdkM*AX+ zNa!M|aaDEOS|J`rg%Vs8aY1=H&1s@|PP2(%H0R?B&XYW4l3}*9SSHcnYXBbxD{Tfd zJMZ=|aDLgpNqhTlRs*zdVVCRuo^HHti8cnSVmlw4zd^R%ww*7pNoVG~>WoDnoFLJGeswvjA_D)37$EX)12k8N)%&p5#KC2Sd~qM ylu9ugv#Fp-h9^~J7gNd*EebWxB}-zqzu`N*2X{OvG~N* - - - - logFormatVersion - 11 - logs - - 8E255990-30E2-4820-98AE-0559FF9CB504 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - 8E255990-30E2-4820-98AE-0559FF9CB504.xcactivitylog - hasPrimaryLog - - primaryObservable - - highLevelStatus - E - totalNumberOfAnalyzerIssues - 0 - totalNumberOfErrors - 1 - totalNumberOfTestFailures - 0 - totalNumberOfWarnings - 3 - - signature - Resolve Packages - timeStartedRecording - 791987515.57282996 - timeStoppedRecording - 791987515.86043 - title - Resolve Packages - uniqueIdentifier - 8E255990-30E2-4820-98AE-0559FF9CB504 - - C9B2536F-0143-4FD7-90C7-C8F37E9F59D6 - - className - IDECommandLineBuildLog - documentTypeString - <nil> - domainType - Xcode.IDEActivityLogDomainType.BuildLog - fileName - C9B2536F-0143-4FD7-90C7-C8F37E9F59D6.xcactivitylog - hasPrimaryLog - - primaryObservable - - highLevelStatus - E - totalNumberOfAnalyzerIssues - 0 - totalNumberOfErrors - 1 - totalNumberOfTestFailures - 0 - totalNumberOfWarnings - 3 - - signature - Resolve Packages - timeStartedRecording - 791987467.25816 - timeStoppedRecording - 791987467.36928797 - title - Resolve Packages - uniqueIdentifier - C9B2536F-0143-4FD7-90C7-C8F37E9F59D6 - - - - diff --git a/_DerivedData/Logs/Test/LogStoreManifest.plist b/_DerivedData/Logs/Test/LogStoreManifest.plist deleted file mode 100644 index f38de44..0000000 --- a/_DerivedData/Logs/Test/LogStoreManifest.plist +++ /dev/null @@ -1,10 +0,0 @@ - - - - - logFormatVersion - 11 - logs - - - diff --git a/keyBoard.xcodeproj/project.pbxproj b/keyBoard.xcodeproj/project.pbxproj index fa89a25..df4ddf0 100644 --- a/keyBoard.xcodeproj/project.pbxproj +++ b/keyBoard.xcodeproj/project.pbxproj @@ -53,7 +53,6 @@ 043FBCD22EAF97630036AFE1 /* KBPermissionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 04C6EAE12EAF940F0089C901 /* KBPermissionViewController.m */; }; 0450AA742EF013D000B6AF06 /* KBEmojiCollectionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0450AA732EF013D000B6AF06 /* KBEmojiCollectionCell.m */; }; 0450AAE22EF03D5100B6AF06 /* KBPerson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450AAE12EF03D5100B6AF06 /* KBPerson.swift */; }; - 0450AB682EF0443E00B6AF06 /* OrderedCollections in Frameworks */ = {isa = PBXBuildFile; productRef = 0450AB672EF0443E00B6AF06 /* OrderedCollections */; }; 0450AC0A2EF11E4400B6AF06 /* StoreKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450AC082EF11E4400B6AF06 /* StoreKitManager.swift */; }; 0450AC0B2EF11E4400B6AF06 /* StoreKitStateConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450ABF82EF11E4400B6AF06 /* StoreKitStateConverter.swift */; }; 0450AC0C2EF11E4400B6AF06 /* SubscriptionLocale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0450ABFF2EF11E4400B6AF06 /* SubscriptionLocale.swift */; }; @@ -99,7 +98,6 @@ 0477BE002EBC6A330055D639 /* HomeRankVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BDFF2EBC6A330055D639 /* HomeRankVC.m */; }; 0477BE042EBC83130055D639 /* HomeMainVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BE032EBC83130055D639 /* HomeMainVC.m */; }; 0477BEA22EBCF0000055D639 /* KBTopImageButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 0477BEA12EBCF0000055D639 /* KBTopImageButton.m */; }; - 04791F8E2ED469C0004E8522 /* KBHostAppLauncher.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791F8D2ED469C0004E8522 /* KBHostAppLauncher.m */; }; 04791F8F2ED469C0004E8522 /* KBHostAppLauncher.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791F8D2ED469C0004E8522 /* KBHostAppLauncher.m */; }; 04791F922ED48010004E8522 /* KBNoticeVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791F912ED48010004E8522 /* KBNoticeVC.m */; }; 04791F952ED48028004E8522 /* KBFeedBackVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 04791F942ED48028004E8522 /* KBFeedBackVC.m */; }; @@ -312,6 +310,7 @@ A1B2C9032FBD000100000001 /* KBBackspaceLongPressHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9022FBD000100000001 /* KBBackspaceLongPressHandler.m */; }; A1B2C9052FBD000200000001 /* KBBackspaceUndoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9042FBD000200000001 /* KBBackspaceUndoManager.m */; }; A1B2C9092FBD000200000005 /* KBInputBufferManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9082FBD000200000004 /* KBInputBufferManager.m */; }; + A1B2C90C2FBD000200000008 /* KBExtensionAppLauncher.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C90B2FBD000200000007 /* KBExtensionAppLauncher.m */; }; A1B2C9262FC9000100000001 /* KBChatMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9212FC9000100000001 /* KBChatMessage.m */; }; A1B2C9272FC9000100000001 /* KBChatMessageCell.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9232FC9000100000001 /* KBChatMessageCell.m */; }; A1B2C9282FC9000100000001 /* KBChatPanelView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C9252FC9000100000001 /* KBChatPanelView.m */; }; @@ -900,6 +899,8 @@ A1B2C9042FBD000200000001 /* KBBackspaceUndoManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBBackspaceUndoManager.m; sourceTree = ""; }; A1B2C9072FBD000200000003 /* KBInputBufferManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBInputBufferManager.h; sourceTree = ""; }; A1B2C9082FBD000200000004 /* KBInputBufferManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBInputBufferManager.m; sourceTree = ""; }; + A1B2C90A2FBD000200000006 /* KBExtensionAppLauncher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBExtensionAppLauncher.h; sourceTree = ""; }; + A1B2C90B2FBD000200000007 /* KBExtensionAppLauncher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBExtensionAppLauncher.m; sourceTree = ""; }; A1B2C9202FC9000100000001 /* KBChatMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatMessage.h; sourceTree = ""; }; A1B2C9212FC9000100000001 /* KBChatMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = KBChatMessage.m; sourceTree = ""; }; A1B2C9222FC9000100000001 /* KBChatMessageCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KBChatMessageCell.h; sourceTree = ""; }; @@ -961,7 +962,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0450AB682EF0443E00B6AF06 /* OrderedCollections in Frameworks */, ECC9EE02174D86E8D792472F /* Pods_keyBoard.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1335,6 +1335,8 @@ A1B2C9042FBD000200000001 /* KBBackspaceUndoManager.m */, A1B2C9072FBD000200000003 /* KBInputBufferManager.h */, A1B2C9082FBD000200000004 /* KBInputBufferManager.m */, + A1B2C90A2FBD000200000006 /* KBExtensionAppLauncher.h */, + A1B2C90B2FBD000200000007 /* KBExtensionAppLauncher.m */, ); path = Utils; sourceTree = ""; @@ -2370,7 +2372,6 @@ mainGroup = 727EC74A2EAF848B00B36487; minimizedProjectReferenceProxies = 1; packageReferences = ( - 0450AB662EF043C000B6AF06 /* XCRemoteSwiftPackageReference "swift-collections" */, ); preferredProjectObjectVersion = 77; productRefGroup = 727EC7542EAF848B00B36487 /* Products */; @@ -2558,7 +2559,6 @@ 04FEDC122F00010000999999 /* KBKeyboardSubscriptionView.m in Sources */, 04FEDC322F00030000999999 /* KBKeyboardSubscriptionFeatureItemView.m in Sources */, 04FEDC422F00040000999999 /* KBKeyboardSubscriptionFeatureMarqueeView.m in Sources */, - 04791F8E2ED469C0004E8522 /* KBHostAppLauncher.m in Sources */, 049FB23B2EC4766700FAB05D /* KBFunctionTagListView.m in Sources */, 049FB23C2EC4766700FAB05D /* KBStreamOverlayView.m in Sources */, 049FB22F2EC34EB900FAB05D /* KBStreamTextView.m in Sources */, @@ -2566,6 +2566,7 @@ A1B2C9032FBD000100000001 /* KBBackspaceLongPressHandler.m in Sources */, A1B2C9052FBD000200000001 /* KBBackspaceUndoManager.m in Sources */, A1B2C9092FBD000200000005 /* KBInputBufferManager.m in Sources */, + A1B2C90C2FBD000200000008 /* KBExtensionAppLauncher.m in Sources */, 049FB23F2EC4B6EF00FAB05D /* KBULBridgeNotification.m in Sources */, 04791F992ED49CE7004E8522 /* KBFont.m in Sources */, 04FC956D2EB054B7007BD342 /* KBKeyboardView.m in Sources */, @@ -2886,7 +2887,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.loveKey.nyx.CustomKeyboard; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -2920,7 +2921,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = com.loveKey.nyx.CustomKeyboard; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -3180,24 +3181,6 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - 0450AB662EF043C000B6AF06 /* XCRemoteSwiftPackageReference "swift-collections" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/apple/swift-collections.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.3.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - 0450AB672EF0443E00B6AF06 /* OrderedCollections */ = { - isa = XCSwiftPackageProductDependency; - package = 0450AB662EF043C000B6AF06 /* XCRemoteSwiftPackageReference "swift-collections" */; - productName = OrderedCollections; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = 727EC74B2EAF848B00B36487 /* Project object */; } diff --git a/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme b/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme index 926a5ea..f06381d 100644 --- a/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme +++ b/keyBoard.xcodeproj/xcshareddata/xcschemes/CustomKeyboard.xcscheme @@ -60,7 +60,7 @@ + RemotePath = "/var/containers/Bundle/Application/2983EA21-E95E-41B4-BE99-C7ACBD71D2AF/keyBoard.app"> #import +#import #ifndef KBSCREEN #define KBSCREEN [UIScreen mainScreen].bounds.size @@ -29,7 +30,7 @@ static __weak UIView *sContainerView = nil; // 缺省承载视图( /// 解析当前 HUD 需要附着的宿主视图 + (UIView *)kb_resolvedHostView { UIView *hostView = sContainerView; -#ifndef KB_APP_EXTENSION +#if !TARGET_OS_EXTENSION // App 内退回到 KeyWindow if (!hostView) { UIWindow *win = nil; diff --git a/keyBoard/Class/Me/VC/MyVC.m b/keyBoard/Class/Me/VC/MyVC.m index 4ce9ecb..0791244 100644 --- a/keyBoard/Class/Me/VC/MyVC.m +++ b/keyBoard/Class/Me/VC/MyVC.m @@ -10,7 +10,9 @@ #import "KBSkinDetailVC.h" #import "KBMyHeaderView.h" // 顶部视图独立封装 #import "KBMyListCell.h" +#if DEBUG #import "KBTestVC.h" +#endif #import "KBNoticeVC.h" #import "KBFeedBackVC.h" #import "KBMyVM.h" @@ -50,7 +52,9 @@ @{ @"title": KBLocalized(@"E-mail"), @"icon": @"my_email_icon", @"color": @(0xFF8A65),@"id":@"4" }, @{ @"title": KBLocalized(@"Agreement"), @"icon": @"my_agreement_icon", @"color": @(0x4CD964),@"id":@"5" }, @{ @"title": KBLocalized(@"Privacy Policy"), @"icon": @"my_privacy_icon", @"color": @(0x5AC8FA),@"id":@"6" }, +#if DEBUG @{ @"title": KBLocalized(@"Test"), @"icon": @"", @"color": @(0x5AC8FA),@"id":@"7" }, +#endif ] ]; @@ -223,9 +227,11 @@ }else if ([itemID isEqualToString:@"8"]){ KBConsumptionRecordVC *vc = [[KBConsumptionRecordVC alloc] init]; [self.navigationController pushViewController:vc animated:true]; +#if DEBUG }else if ([itemID isEqualToString:@"7"]){ KBTestVC *vc = [[KBTestVC alloc] init]; [self.navigationController pushViewController:vc animated:true]; +#endif } } diff --git a/keyBoard/Class/Shared/KBConfig.h b/keyBoard/Class/Shared/KBConfig.h index 962b76e..520225f 100644 --- a/keyBoard/Class/Shared/KBConfig.h +++ b/keyBoard/Class/Shared/KBConfig.h @@ -10,20 +10,18 @@ // 基础baseUrl #ifndef KB_BASE_URL -#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/" +#define KB_BASE_URL @"https://devcallback.loveamorkey.com/api" #endif // Universal Links 通用链接 #ifndef KB_UL_BASE -#define KB_UL_BASE @"https://your.domain/ul" +#define KB_UL_BASE @"https://app.tknb.net/ul" #endif #define KB_UL_LOGIN KB_UL_BASE @"/login" #define KB_UL_SETTINGS KB_UL_BASE @"/settings" -// 充值入口的通用链接:当前复用 /login 路径,通过 query 区分(避免额外配置 AASA 路径) -#define KB_UL_RECHARGE KB_UL_LOGIN - -#endif /* KBConfig_h */ +// 充值入口的通用链接 +#define KB_UL_RECHARGE KB_UL_BASE @"/recharge" // --- 认证/共享钥匙串 配置 --- // 若已在 Capabilities 中启用 Keychain Sharing,并添加访问组: @@ -34,30 +32,9 @@ #define KB_KEYCHAIN_ACCESS_GROUP @"TN6HHV45BB.com.loveKey.nyx.shared" #endif -// --- 设备特性:是否为带刘海机型(iPhone X 及以后异形屏)--- -// 说明:在 iPhone 12 等机型底部会有 34px 安全区,这里通过安全区来判断是否为“刘海屏”。 -// 注意:使用到 UIKit,这里自行引入,避免依赖 PCH 的包含顺序。 -#import -static inline BOOL KBDeviceHasNotchRuntime(void) { - if (@available(iOS 11.0, *)) { - UIWindow *window = nil; - if (@available(iOS 13.0, *)) { - for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { - if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) { - UIWindowScene *ws = (UIWindowScene *)scene; - for (UIWindow *w in ws.windows) { if (w.isKeyWindow) { window = w; break; } } - if (window) { break; } - } - } - if (!window) { window = UIApplication.sharedApplication.windows.firstObject; } - } else { - window = UIApplication.sharedApplication.keyWindow; - } - return window.safeAreaInsets.bottom > 0.0; - } - return NO; -} +// 说明: +// - 该头文件同时被主 App 与键盘扩展引用; +// - 设备/窗口相关的 UIKit 辅助方法(如 UIApplication.sharedApplication)在扩展中不可用, +// 请放到主 App 的前缀头或具体业务代码中,避免引入扩展不允许的 API。 -#ifndef KB_DEVICE_HAS_NOTCH -#define KB_DEVICE_HAS_NOTCH (KBDeviceHasNotchRuntime()) -#endif +#endif /* KBConfig_h */ diff --git a/keyBoard/Info.plist b/keyBoard/Info.plist index c1ce7ad..7074000 100644 --- a/keyBoard/Info.plist +++ b/keyBoard/Info.plist @@ -18,8 +18,10 @@ NSAppTransportSecurity NSAllowsArbitraryLoads - + + NSMicrophoneUsageDescription + 需要使用麦克风进行语音输入 UIDesignRequiresCompatibility diff --git a/keyBoard/Shared/KBConfig.h b/keyBoard/Shared/KBConfig.h index 962b76e..520225f 100644 --- a/keyBoard/Shared/KBConfig.h +++ b/keyBoard/Shared/KBConfig.h @@ -10,20 +10,18 @@ // 基础baseUrl #ifndef KB_BASE_URL -#define KB_BASE_URL @"https://m1.apifoxmock.com/m1/5438099-5113192-default/" +#define KB_BASE_URL @"https://devcallback.loveamorkey.com/api" #endif // Universal Links 通用链接 #ifndef KB_UL_BASE -#define KB_UL_BASE @"https://your.domain/ul" +#define KB_UL_BASE @"https://app.tknb.net/ul" #endif #define KB_UL_LOGIN KB_UL_BASE @"/login" #define KB_UL_SETTINGS KB_UL_BASE @"/settings" -// 充值入口的通用链接:当前复用 /login 路径,通过 query 区分(避免额外配置 AASA 路径) -#define KB_UL_RECHARGE KB_UL_LOGIN - -#endif /* KBConfig_h */ +// 充值入口的通用链接 +#define KB_UL_RECHARGE KB_UL_BASE @"/recharge" // --- 认证/共享钥匙串 配置 --- // 若已在 Capabilities 中启用 Keychain Sharing,并添加访问组: @@ -34,30 +32,9 @@ #define KB_KEYCHAIN_ACCESS_GROUP @"TN6HHV45BB.com.loveKey.nyx.shared" #endif -// --- 设备特性:是否为带刘海机型(iPhone X 及以后异形屏)--- -// 说明:在 iPhone 12 等机型底部会有 34px 安全区,这里通过安全区来判断是否为“刘海屏”。 -// 注意:使用到 UIKit,这里自行引入,避免依赖 PCH 的包含顺序。 -#import -static inline BOOL KBDeviceHasNotchRuntime(void) { - if (@available(iOS 11.0, *)) { - UIWindow *window = nil; - if (@available(iOS 13.0, *)) { - for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) { - if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) { - UIWindowScene *ws = (UIWindowScene *)scene; - for (UIWindow *w in ws.windows) { if (w.isKeyWindow) { window = w; break; } } - if (window) { break; } - } - } - if (!window) { window = UIApplication.sharedApplication.windows.firstObject; } - } else { - window = UIApplication.sharedApplication.keyWindow; - } - return window.safeAreaInsets.bottom > 0.0; - } - return NO; -} +// 说明: +// - 该头文件同时被主 App 与键盘扩展引用; +// - 设备/窗口相关的 UIKit 辅助方法(如 UIApplication.sharedApplication)在扩展中不可用, +// 请放到主 App 的前缀头或具体业务代码中,避免引入扩展不允许的 API。 -#ifndef KB_DEVICE_HAS_NOTCH -#define KB_DEVICE_HAS_NOTCH (KBDeviceHasNotchRuntime()) -#endif +#endif /* KBConfig_h */