122 lines
3.5 KiB
Objective-C
122 lines
3.5 KiB
Objective-C
//
|
|
// KBExtensionAppLauncher.m
|
|
// CustomKeyboard
|
|
//
|
|
|
|
#import "KBExtensionAppLauncher.h"
|
|
#import <objc/message.h>
|
|
|
|
@implementation KBExtensionAppLauncher
|
|
|
|
+ (void)openPrimaryURL:(NSURL * _Nullable)primaryURL
|
|
fallbackURL:(NSURL * _Nullable)fallbackURL
|
|
usingInputController:(UIInputViewController *)ivc
|
|
source:(UIResponder *)source
|
|
completion:(void (^ _Nullable)(BOOL success))completion {
|
|
if (!ivc || (!primaryURL && !fallbackURL)) {
|
|
if (completion) { completion(NO); }
|
|
return;
|
|
}
|
|
|
|
// 保证在主线程回调,避免调用方再做一次 dispatch。
|
|
void (^finish)(BOOL) = ^(BOOL ok){
|
|
if (!completion) return;
|
|
if ([NSThread isMainThread]) {
|
|
completion(ok);
|
|
} else {
|
|
dispatch_async(dispatch_get_main_queue(), ^{ completion(ok); });
|
|
}
|
|
};
|
|
|
|
NSURL *first = primaryURL ?: fallbackURL;
|
|
NSURL *second = (first == primaryURL) ? fallbackURL : nil;
|
|
|
|
if (!first) {
|
|
finish(NO);
|
|
return;
|
|
}
|
|
|
|
[ivc.extensionContext openURL:first completionHandler:^(BOOL ok) {
|
|
if (ok) {
|
|
finish(YES);
|
|
return;
|
|
}
|
|
|
|
if (second) {
|
|
[ivc.extensionContext openURL:second completionHandler:^(BOOL ok2) {
|
|
if (ok2) {
|
|
finish(YES);
|
|
return;
|
|
}
|
|
BOOL bridged = [self p_bridgeFirst:first second:second from:source];
|
|
finish(bridged);
|
|
}];
|
|
} else {
|
|
BOOL bridged = [self p_bridgeFirst:first second:nil from:source];
|
|
finish(bridged);
|
|
}
|
|
}];
|
|
}
|
|
|
|
+ (void)openScheme:(NSURL *)scheme
|
|
usingInputController:(UIInputViewController *)ivc
|
|
source:(UIResponder *)source
|
|
completion:(void (^ _Nullable)(BOOL success))completion {
|
|
[self openPrimaryURL:scheme
|
|
fallbackURL:nil
|
|
usingInputController:ivc
|
|
source:source
|
|
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
|