本文Demo同步Githubhtml
前言ios
为啥我一个作社交、直播、图片后编辑方向的iOS开发忽然想学输入法开发呢,这一切还得从我看到搜狗输入法的招聘JD提及....git
我看到搜狗输入法的招聘里写到一条:
了解逆向优先
,此时我有个疑问,作输入法App开发和逆向有什么关系? 因而就有了想了解输入法App开发的兴趣,也就有个这篇文章github首先在写这个前言的时候,我是压根不知道输入法怎么开发的。当想到要作一个输入法App时,我有以下疑问json
- 为何安装了搜狗输入法App后,系统键盘设置里会出现搜狗输入法,且有彻底访问的选项(UISwith),彻底访问是作什么的?
- 为何安装了搜狗输入法App后,在我本身的App里调起输入法后,调起的是第三方的搜狗输入法键盘
- 搜狗输入法App和搜狗输入法键盘二者之间有什么关联? 如何关联?
- 第三方输入法和逆向之间有什么关联?逆向知识能为输入法类App带来什么?
若是你也有以上疑问,请耐心看下去(其实写到此处时能不能解答我也不知道... 下午均有解答)xcode
本文目录:服务器
- 调研着手
- App实现(简单实现一个能在其它App内使用的输入法App)
- 输入法类App原理
- 输入法App和逆向(我的瞎猜,可能鲁迅人自己根本没这么想)
由于了解输入法App的念头起源于逆向
,因此此处我先砸壳,看看什么发现...markdown
在砸完搜狗输入法
壳后,看ipa内部的文件,我并无看到与其它App有什么区别,无非是icon资源
,infoPlist
,.mpa资源
,Frameworks
、lottie json文件夹
、开启彻底访问的.mp4教程
、MJRefresh等第三方库
、/PlugIns/SogouAction.appex
以及mach-o
。网络
经过ipa,我发现两个关键点:数据结构
plugins
路径下,有一个SogouAction.appex
文件,.appex
通常是iOS拓展Extension
生成的文件,好比接入NotificationService
一样会生成push.appex
文件此时,我对百度输入法
一样进行砸壳操做,发如今PlugIns
文件夹下,一样有BaiduInputMethod.appex
、NotificationContent.appex
这两个.appex结尾的文件
此时根据直觉,我以为输入法类App的关键在添加了一个相似于inputKeyboard
的拓展Extension
实现
头文件里搜索了SogouAction
仍然一无所得
简单看了下搜狗的UI架构,想经过Reveal看看能不能找出对应的class,结果不但没找到,Reveal里压根没显示出来键盘的UI(键盘是系统层UI,全部Reveal不到)。想到咱们的目标是了解如何开发一个输入法App。此时咱们暂时中止逆向,直接去百度...
百度搜到的东西不多,大可能是检测键盘弹出高度,只搜到关键性的三篇文章:
其中文章一是苹果官方文档,主要讲构建输入法App用到的API 其中文章二主要将输入法App的App架构与通讯,经过此文章咱们大概知道为何搜狗的iOS须要逆向经验 其中文章三是构建一个简单的输入法App 我决定跟着文章三开发一个简单的App,了解其原理
拓展Target命名为CustomKeyboard
弹出Activate “CustomKeyboard” scheme?
选择activate
此时咱们注意到,添加CustomKeyboard后,默认生成了一个类KeyboardViewController
!!! 注意一个很容易忽视的问题:记得修改CustomKeyboard
Target支持的最低版本,若是支持的最低版本高于设备版本,xcode编译时不会报错,但运行时这个target不会运行,添加NotificationService时一样
有这个容易忽视的问题
此时咱们启动App,在设置-通用-键盘-添加键盘里能看到咱们的自定义键盘,同时,也有开启彻底访问的选项
添加键盘后,咱们将键盘切换到咱们自定义的键盘,任意App内调起键盘能够看到如图
此时弹出咱们的自定义键盘,能够看到,键盘有一个添加Extension时默认生成代码的button。咱们将会在KeyboardViewController
类里作键盘的自定义布局
我把源码上传到了Github CCInput
键盘的UI布局我以简单以搜狗输入法的数字键盘为例,这里附上代码说明,具体请查看Demo代码
KeyboardViewController
建立keyboard extension时,系统自动建立的vc,继承自UIInputViewController
,键盘的布局、逻辑处理都在此类中
CCLeftTableView
左侧符号输入,是一个TabView,,支持增长符号数据源
CCCenterView
中间数字键盘及底部切换、数字0、空格功能
CCRightView
右侧删除、句号、@符号、换行功能
CCKeyboardModel
键盘数据源Model,Model有两个属性,分别是
NSString *string
用于键盘按钮文本的展现CCKeyboardAction keyboardAction
是点击事件的枚举类型,点击键盘的时候经过此属性统一处理简单的写了个经过runtime自动获取属性解析json为model的方法
主要是UI和数据结构,此处不深刻探究,感兴趣请看demo,此处有个自定义键盘高度的坑注意下:
- 经过简单的setframe没法更改键盘默认高度
- 须要经过设置
NSLayoutConstraint
的方式,切在viewDidLoad方法中设置无效- 需在
viewDidAppear
以前设置键盘高度
这个问题的解决方案由于国内作键盘的公司比较少,百度搜索不到相关资料,附上stackoverflow连接 stackoverflow: iOS 8 Custom Keyboard: Changing the Height
Demo效果:
同时附上Demo中代码
static CGFloat KEYBOARDHEIGHT = 256;
@interface KeyboardViewController ()
<CCTopBarDelegate,
CCLeftViewDelegate,
CCCenterViewDelegate,
CCRightViewDelegate>
/// 用于设置键盘自定义高度
@property (nonatomic, assign) NSLayoutConstraint *heightConstraint;
@end
@implementation KeyboardViewController
- (void)prepareHeightConstraint {
if (self.heightConstraint == nil) {
UILabel *dummyView = [[UILabel alloc] initWithFrame:CGRectZero];
dummyView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:dummyView];
self.heightConstraint = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.0 constant:KEYBOARDHEIGHT];
self.heightConstraint.priority = 750;
[self.view addConstraint:self.heightConstraint];
} else {
self.heightConstraint.constant = KEYBOARDHEIGHT;
}
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self prepareHeightConstraint];
}
/// 重写的父类方法,苹果建议在此处updateViewConstraints
- (void)updateViewConstraints {
[super updateViewConstraints];
if (self.view.frame.size.width == 0 && self.view.frame.size.height == 0) {
return;
}
[self prepareHeightConstraint];
}
复制代码
UIInputViewController 涉及到的方法说明:
advanceToNextInputMode
切换到下一个输入法dismissKeyboard
退出键盘(至关于resignFirstResponder
)insertText
插入文本(尾部)deleteBackward
删除输入框内上一个文本第三方输入法,分为主程序(containing App)、键盘(extension), 分别对应Demo中的InputApp
、CustomKeyboard
, 主程序在桌面可见,Host App则是使用输入法的其它App,
主程序和键盘Extension正常状况是没法共享数据的(沙箱隔离)
在开启彻底访问时,主程序和键盘Extension能够共享数据(经过app groups)
主程序和Host App没法共享数据(沙箱隔离)
键盘Extension和Host App只能共享文本数据(经过系统UITextDocumentProxy)
在设置-通用-键盘中,苹果有对彻底访问作说明,摘录以下
第三方键盘提供了另外一种途径来键入键盘数据。这些键盘能够访问您键入的全部数据,包括银行帐户、信用卡号码、街道地址及其余我的信息与敏感信息。这些键盘还可能访问相邻文本及数据,这些信息对改进自动更正功能卓有帮助。
若是您启用“彻底访问”,开发者即得到许可访问、收集与传输您键入的数据。此外,若是附带键盘的第三方应用程序得到您的许可访问地理位置信息、照片、或其余我的数据,那么此键盘也可收集并将该信息传输至键盘开发者的服务器上。若是您停用某第三方键盘的“彻底访问”,以后再从新启用,那么键盘开发者则可能可以访问、收集并传输网络访问禁用期间所键入的信息。
若是您不启用“彻底访问”,那么开发者则不可收集与传输您键入的数据。未经您许可,任何未受权的数据收集或传输行为均违反其开发者协议。此外,技术限制一样在防止未经许可的访问方面起做用。任何试图破坏此类限制的尝试一样违反开发者协议。
你任什么时候候都可选择停用第三方键盘。打开“设置”,轻点“键盘”,将该键盘从键盘列表中移除便可。
若是您使用第三方键盘,即需遵照键盘开发者的条款、隐私政策及作法。使用此类键盘App与服务以前,您应仔细阅读其条款、隐私政策和作法,以了解他们如何使用您的数据及其余信息。
总结就是,只有开启了彻底访问,输入法App才能:
官方文档:Sharing Data with Your Containing App
简单来讲,开启了app groups,至关于生成一个中间数据共享区(shared container)来将App's container
和Extension's container
的数据关联并共享
经过如下方式就能够共享数据
// Create and share access to an NSUserDefaults object
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: @"com.example.domain.MyShareExtension"];
// Use the shared user defaults object to update the user's account
[mySharedDefaults setObject:theAccountName forKey:@"lastAccountName"];
复制代码
经过上文3.1
3.2
3.3
咱们知道, 主App和Extension之间要想共享数据,必须开启彻底访问经过App Groups的方式共享数据,对于一款键盘类App,他确定是但愿App和Extension能实现数据共享,以达到如下需求:
可是因为沙箱的限制,若是用户没有开启彻底访问,以上三点需求就达不成了。事实上以上三点需求对我这样的用户来讲很是重要,无缝衔接切换设备的快感是没法形容的,好比当我在iPhone上输入ma
后,我选择了联想词汇表中的码代码的小马
,而后当我在mac上使用搜狗输入法一样输入ma
,mac也联想到了码代码的小马
,这样会将个人输入效率提升不少
此时就用到了逆向技术 苹果有沙箱隔离,逆向里有沙箱逃脱(沙箱逃脱详细解释请参考我以前文章沙箱逃脱)
沙箱逃脱简单来讲就是跨进程访问数据共享数据,放在这里就是,即便用户不开启彻底访问,主App也能访问Extension的数据,以实现如上三点需求。
只是目前沙箱逃脱技术只能在越狱设备实现,也许将来某一天某个大牛实现了在未越狱设备的沙箱逃脱,那对于逆向开发者来讲,简直是这盛世如你所愿,大好河山任你看...