iOS Keyboard Extension 开发笔记

keyboard_architecture_2x.png

Apple 在 iOS 8 里就引入了 Keyboard Extension,但网上相关但开发但资料很少,我在开发中也遇到了不少坑,为了给大家分享下这方面但知识,所以才有了这篇文章。
Custom Keyboard 要实现起来也非常简单,我们只需要在项目里新建一个 Custom Keyboard Extension 的 Target,Xcode 就自动会给我们创建一个 KeyboardViewController,开发者通过这个类就可以做简单的开发了。但是往往实际情况并没有那么简单。我们可能需要在键盘请求网络数据,或者和Containing App 通信等等,这时我们会遇到很多问题。这边文章我会讲述一下常见的问题和解法。


概要

  • Extension 如何通讯
  • 检测键盘是否已经添加
  • 检测键盘是否激活
  • 播放系统声音
  • 检测是否已经获取(最高访问权限) RequestsOpenAccess
  • 键盘切换方法

Extension 如何通讯

detailed_communication_2x.png

这是苹果官方给出的一张图,Containing App 是我们的主 App, Host app 是 Extension 所运行的第三方 App(比如微信),为了方便理解下面我们会把 Containing App 称为“主 App”,Host App 称为“第三方 App”。

总结如下:

  1. App extension 无法和第三方 App 直接通信(简单理解它们就是两个不同的进程,extension 启动了并不代表主App也会启动)
  2. App extension 可以通过 open URL 和主 App通信,但这条链路只是单向的
  3. 主 App 和 App extension 可以通过读写共同的文件资源来通信(比如 UserDefault)
  4. 另外一个方式这里没提到:就是可以用更底层的 DarwinNotify 来建立 Extension 和 Containing app 之间的通讯,可以参考下面的👇开源库

choefele/CCHDarwinNotificationCenter

通过 App Group 共同维护 UserDefault 是一种比较简单的通讯方法。但是开发者也需要注意的是如果我们的键盘没有获取到没有完全访问权限,键盘是只能读取,没法修改 UserDefault 的值的(如果这个 UserDefault 是 Containing app 创建的)。另外一点是 DarwinNotify 现在也需要完全访问权限了。

最后给大家一个忠告: 千万一定要在真机上调试!

检测键盘是否已经添加

var isKeyboardEnabled: Bool {
    guard let keyboards = UserDefaults.standard.object(forKey: "AppleKeyboards") as? [String] else {
        return false
    }
    return keyboards.contains("你的 extension bundle id")
}

检测键盘是否已经激活

键盘类型的应用往往有个需求是:当用户第一次切换到我们开发的 Keyboard,需要在主 App 显示欢迎👏图文或者开启引导流程。我们可以通过 openURL 的方式来通知主App。

// Step1: 在 keyboard 中调用
// 打开主APP,比如 openURL(scheme:"yourAppScheme://actived")
func openURL(scheme: String) {
    let url = URL(string: scheme)!
    let context = NSExtensionContext()
    context.open(url, completionHandler: nil)
    var responder = self as UIResponder?
    let selectorOpenURL = sel_registerName("openURL:")
    while (responder != nil) {
        if responder!.responds(to: selectorOpenURL) {
            responder!.perform(selectorOpenURL, with: url)
            break
        }
        responder = responder?.next
    }
}

// step2:
// app 在前台的时候接收通知
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    if url.scheme == "yourAppScheme" && url.host == "actived" {
        // do something
    }
    return true
}

这里要注意的是openURL 会唤起主 App(如果主 App 不在前台),所以这里需要额外做判断。

播放系统声音

// 直接上代码
// 点击: SystemSoundID = 1123
// 删除按钮: SystemSoundID = 1155
// 系统键盘: SystemSoundID = 1156
AudioServicesPlaySystemSound(SystemSoundID)

检测是否已经获取(最高访问权限) RequestsOpenAccess

// Keyboard Extension
override var hasFullAccess: Bool {
    if #available(iOS 11.0, *) {
        return super.hasFullAccess// super is UIInputViewController.
    }
    if #available(iOS 10.0, *) {
        let original: String? = UIPasteboard.general.string
        UIPasteboard.general.string = " "
        let val: Bool = UIPasteboard.general.hasStrings
        if let str = original {
            UIPasteboard.general.string = str
        }
        return val
    }
    return UIPasteboard.general.isKind(of: UIPasteboard.self)
}

主 App 没有直接获取的方法,但我们可以通过上面提到的通讯方法,在 Keyboard Extension 中把状态传过去

全面屏幕隐藏切换 keyboard 按键

在全屏但设备系统会给我们默认提供切换键盘但按钮,所以我们开发的键盘就不需要提供此按钮了,我们可以通过以下方式来判断:

// needsInputModeSwitchKey
var needsSwitchKey: Bool {
    if #available(iOSApplicationExtension 11.0, *) {
        return needsInputModeSwitchKey
    } else {
        return true
    }
}

其他资料

Custom Keyboard

Custom Keyboards - Extensions - iOS - Human Interface Guidelines - Apple Developer

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352