本文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的數據,以實現如上三點需求。
只是目前沙箱逃脫技術只能在越獄設備實現,也許將來某一天某個大牛實現了在未越獄設備的沙箱逃脫,那對於逆向開發者來講,簡直是這盛世如你所願,大好河山任你看...