Google的自定义iOS应用Gboard有一项有趣的功能,无法在iOS SDK中使用公共API(从iOS 10开始)。 我想知道Google究竟如何完成以编程方式弹出Gboard中应用切换堆栈中的一个应用的任务。
自定义iOS键盘有两个主要组件:容器应用和键盘应用扩展。键盘应用程序扩展程序在单独的操作系统进程中运行,只要用户在手机上需要输入文本的任何应用程序中,该进程就会启动。
这些是使用Gboard可以遵循的近似步骤,以查看以编程方式返回到以前的应用程序的效果:
我认为Google正在使用私有API,方法是使用Objective-C运行时内省探索状态栏的视图层次结构,并以某种方式合成tap事件或调用公开的目标/操作。我对此进行了很少的探索,并且能够在状态栏中找到有趣的UIView子类,例如包含UIStatusBarBreadcrumbItemView数组的UISystemNavigationAction。我继续探索这些课程,希望能找到一些复制用户交互的方法。
我了解使用私有API是让您的应用提交从App Store中被拒绝的好方法 - 这不是我想在答案中解决的问题。我主要是关于Google如何完成以GET方式在App切换堆栈中以编程方式弹出一个应用程序的具体任务的具体答案。
答案 0 :(得分:35)
...虽然不是通过探索视图层次结构或事件注入。
当语音到文本操作完成后,我们可以从Xcode或Console中检查它调用-[AVAudioSession setActive:withOptions:error:]
方法的syslog。所以我对Gboard应用程序进行了逆向工程,并寻找与此相关的堆栈跟踪。
爬上调用堆栈,我们可以找到-[GKBVoiceRecognitionViewController navigateBackToPreviousApp]
方法,然后......
…_systemNavigationAction
?是的,绝对是私有API。
由于class_getInstanceVariable
是公共API且"_systemNavigationAction"
是字符串文字,因此自动检查程序无法记录私有API使用情况,而人工审核者可能看不到任何错误。 “跳回以前的应用程序”的行为。或者可能是因为他们是谷歌而你不是......
执行“跳回上一个应用程序”操作的实际代码如下:
@import UIKit;
@import ObjectiveC.runtime;
@interface UISystemNavigationAction : NSObject
@property(nonatomic, readonly, nonnull) NSArray<NSNumber*>* destinations;
-(BOOL)sendResponseForDestination:(NSUInteger)destination;
@end
inline BOOL jumpBackToPreviousApp() {
Ivar sysNavIvar = class_getInstanceVariable(UIApplication.class, "_systemNavigationAction");
UIApplication* app = UIApplication.sharedApplication;
UISystemNavigationAction* action = object_getIvar(app, sysNavIvar);
if (!action) {
return NO;
}
NSUInteger destination = action.destinations.firstObject.unsignedIntegerValue;
return [action sendResponseForDestination:destination];
}
特别是,-sendResponseForDestination:
方法执行实际的“返回”操作。
(由于API未记录,Gboard实际上使用的API 不正确。他们使用了错误的签名-(void)sendResponseForDestination:(id)destination
。但是,1
以外的所有数字都会发生工作相同,所以谷歌开发人员这次很幸运)