iOS - iOS - 第三方輸入法App原理調研、App Group數據共享

本文Demo同步Githubhtml

前言ios

爲啥我一個作社交、直播、圖片後編輯方向的iOS開發忽然想學輸入法開發呢,這一切還得從我看到搜狗輸入法的招聘JD提及....git

我看到搜狗輸入法的招聘裏寫到一條:瞭解逆向優先,此時我有個疑問,作輸入法App開發和逆向有什麼關係? 因而就有了想了解輸入法App開發的興趣,也就有個這篇文章github

首先在寫這個前言的時候,我是壓根不知道輸入法怎麼開發的。當想到要作一個輸入法App時,我有以下疑問json

  1. 爲何安裝了搜狗輸入法App後,系統鍵盤設置裏會出現搜狗輸入法,且有徹底訪問的選項(UISwith),徹底訪問是作什麼的?
  2. 爲何安裝了搜狗輸入法App後,在我本身的App裏調起輸入法後,調起的是第三方的搜狗輸入法鍵盤
  3. 搜狗輸入法App和搜狗輸入法鍵盤二者之間有什麼關聯? 如何關聯?
  4. 第三方輸入法和逆向之間有什麼關聯?逆向知識能爲輸入法類App帶來什麼?

若是你也有以上疑問,請耐心看下去(其實寫到此處時能不能解答我也不知道... 下午均有解答)xcode

本文目錄:服務器

  1. 調研着手
  2. App實現(簡單實現一個能在其它App內使用的輸入法App)
  3. 輸入法類App原理
  4. 輸入法App和逆向(我的瞎猜,可能魯迅人自己根本沒這麼想)

1. 調研着手

1.1 砸殼

由於瞭解輸入法App的念頭起源於逆向,因此此處我先砸殼,看看什麼發現...markdown

在砸完搜狗輸入法殼後,看ipa內部的文件,我並無看到與其它App有什麼區別,無非是icon資源,infoPlist,.mpa資源,Frameworkslottie json文件夾開啓徹底訪問的.mp4教程MJRefresh等第三方庫/PlugIns/SogouAction.appex以及mach-o網絡

經過ipa,我發現兩個關鍵點:數據結構

  1. 搜狗工程師比較喜歡用plist作配置類數據存儲(這點我以爲挺挺好的,便於管理,固然用.json也沒問題)
  2. plugins路徑下,有一個SogouAction.appex文件,.appex通常是iOS拓展Extension生成的文件,好比接入NotificationService一樣會生成push.appex文件

此時,我對百度輸入法一樣進行砸殼操做,發如今PlugIns文件夾下,一樣有BaiduInputMethod.appexNotificationContent.appex這兩個.appex結尾的文件

此時根據直覺,我以爲輸入法類App的關鍵在添加了一個相似於inputKeyboard的拓展Extension實現

1.2 class-dump

頭文件裏搜索了SogouAction仍然一無所得

1.3 Reveal

簡單看了下搜狗的UI架構,想經過Reveal看看能不能找出對應的class,結果不但沒找到,Reveal裏壓根沒顯示出來鍵盤的UI(鍵盤是系統層UI,全部Reveal不到)。想到咱們的目標是瞭解如何開發一個輸入法App。此時咱們暫時中止逆向,直接去百度...

1.3 利用搜索引擎

百度搜到的東西不多,大可能是檢測鍵盤彈出高度,只搜到關鍵性的三篇文章:

Keyboards and Input

iOS輸入法_開發系統架構

iOS輸入法開發(Swift)

其中文章一是蘋果官方文檔,主要講構建輸入法App用到的API 其中文章二主要將輸入法App的App架構與通訊,經過此文章咱們大概知道爲何搜狗的iOS須要逆向經驗 其中文章三是構建一個簡單的輸入法App 我決定跟着文章三開發一個簡單的App,瞭解其原理

2. App搭建

2.1 建立一個名爲InputApp的項目(爲了練練手,咱們採用OC編碼,與連接內不一樣,後續我應該會建立Swift版本上傳Github)

建立項目

2.2 建立鍵盤Target

拓展Target命名爲CustomKeyboard

CustomKeyboard

彈出Activate 「CustomKeyboard」 scheme? 選擇activate

此時咱們注意到,添加CustomKeyboard後,默認生成了一個類KeyboardViewController

image.png

!!! 注意一個很容易忽視的問題:記得修改CustomKeyboard Target支持的最低版本,若是支持的最低版本高於設備版本,xcode編譯時不會報錯,但運行時這個target不會運行,添加NotificationService時一樣有這個容易忽視的問題

此時咱們啓動App,在設置-通用-鍵盤-添加鍵盤裏能看到咱們的自定義鍵盤,同時,也有開啓徹底訪問的選項

<> <>

添加鍵盤後,咱們將鍵盤切換到咱們自定義的鍵盤,任意App內調起鍵盤能夠看到如圖

<>

此時彈出咱們的自定義鍵盤,能夠看到,鍵盤有一個添加Extension時默認生成代碼的button。咱們將會在KeyboardViewController類裏作鍵盤的自定義佈局

2.3 鍵盤UI佈局

我把源碼上傳到了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,此處有個自定義鍵盤高度的坑注意下:

  1. 經過簡單的setframe沒法更改鍵盤默認高度
  2. 須要經過設置NSLayoutConstraint的方式,切在viewDidLoad方法中設置無效
  3. 需在viewDidAppear以前設置鍵盤高度

這個問題的解決方案由於國內作鍵盤的公司比較少,百度搜索不到相關資料,附上stackoverflow連接 stackoverflow: iOS 8 Custom Keyboard: Changing the Height

Demo效果:

Demo效果.gif

同時附上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 刪除輸入框內上一個文本

3. 輸入法類App原理

3.1 主程序與鍵盤拓展的關係

圖源自網絡

第三方輸入法,分爲主程序(containing App)、鍵盤(extension), 分別對應Demo中的InputAppCustomKeyboard, 主程序在桌面可見,Host App則是使用輸入法的其它App,

  • 主程序和鍵盤Extension正常狀況是沒法共享數據的(沙箱隔離)

  • 在開啓徹底訪問時,主程序和鍵盤Extension能夠共享數據(經過app groups)

  • 主程序和Host App沒法共享數據(沙箱隔離)

  • 鍵盤Extension和Host App只能共享文本數據(經過系統UITextDocumentProxy)

3.2 徹底訪問

在設置-通用-鍵盤中,蘋果有對徹底訪問作說明,摘錄以下

第三方鍵盤提供了另外一種途徑來鍵入鍵盤數據。這些鍵盤能夠訪問您鍵入的全部數據,包括銀行帳戶、信用卡號碼、街道地址及其餘我的信息與敏感信息。這些鍵盤還可能訪問相鄰文本及數據,這些信息對改進自動更正功能卓有幫助。

若是您啓用「徹底訪問」,開發者即得到許可訪問、收集與傳輸您鍵入的數據。此外,若是附帶鍵盤的第三方應用程序得到您的許可訪問地理位置信息、照片、或其餘我的數據,那麼此鍵盤也可收集並將該信息傳輸至鍵盤開發者的服務器上。若是您停用某第三方鍵盤的「徹底訪問」,以後再從新啓用,那麼鍵盤開發者則可能可以訪問、收集並傳輸網絡訪問禁用期間所鍵入的信息。

若是您不啓用「徹底訪問」,那麼開發者則不可收集與傳輸您鍵入的數據。未經您許可,任何未受權的數據收集或傳輸行爲均違反其開發者協議。此外,技術限制一樣在防止未經許可的訪問方面起做用。任何試圖破壞此類限制的嘗試一樣違反開發者協議。

你任什麼時候候都可選擇停用第三方鍵盤。打開「設置」,輕點「鍵盤」,將該鍵盤從鍵盤列表中移除便可。

若是您使用第三方鍵盤,即需遵照鍵盤開發者的條款、隱私政策及作法。使用此類鍵盤App與服務以前,您應仔細閱讀其條款、隱私政策和作法,以瞭解他們如何使用您的數據及其餘信息。

總結就是,只有開啓了徹底訪問,輸入法App才能:

  • 訪問、收集、傳輸輸入的數據
  • 訪問並上傳位置、照片、我的數據(通信錄之類權限隱私數據)
  • 若是開啓以後又關閉,那麼開發者仍是可以傳輸禁用期間的數據
  • 若是不開啓,那麼由於蘋果的技術限制(沙箱),開發者並不能上傳鍵盤Extension獲取到的數據

3.3 App Groups數據共享

官方文檔:Sharing Data with Your Containing App

app_extensions_container_restrictions_2x.png

簡單來講,開啓了app groups,至關於生成一箇中間數據共享區(shared container)來將App's containerExtension'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"];
複製代碼

4 輸入法App和逆向(我的瞎猜,可能魯迅人自己根本沒這麼想)

經過上文3.1 3.2 3.3咱們知道, 主App和Extension之間要想共享數據,必須開啓徹底訪問經過App Groups的方式共享數據,對於一款鍵盤類App,他確定是但願App和Extension能實現數據共享,以達到如下需求:

  • 實時更新網絡高頻熱詞,用於聯想輸入
  • 共享鍵盤皮膚
  • 存儲用戶高頻輸入的詞彙、通信錄等數據,上傳服務端,當用戶換設備時,能同步並共享給新設備的Extension

可是因爲沙箱的限制,若是用戶沒有開啓徹底訪問,以上三點需求就達不成了。事實上以上三點需求對我這樣的用戶來講很是重要,無縫銜接切換設備的快感是沒法形容的,好比當我在iPhone上輸入ma後,我選擇了聯想詞彙表中的碼代碼的小馬,而後當我在mac上使用搜狗輸入法一樣輸入ma,mac也聯想到了碼代碼的小馬,這樣會將個人輸入效率提升不少

此時就用到了逆向技術 蘋果有沙箱隔離,逆向裏有沙箱逃脫(沙箱逃脫詳細解釋請參考我以前文章沙箱逃脫)

沙箱逃脫簡單來講就是跨進程訪問數據共享數據,放在這裏就是,即便用戶不開啓徹底訪問,主App也能訪問Extension的數據,以實現如上三點需求。

只是目前沙箱逃脫技術只能在越獄設備實現,也許將來某一天某個大牛實現了在未越獄設備的沙箱逃脫,那對於逆向開發者來講,簡直是這盛世如你所願,大好河山任你看...

相關文章
相關標籤/搜索