7f059dbd45
Migrate keyboard dictation to continuous Flow sessions with auto-start, tap-to-toggle recording, 60s countdown, five-step onboarding, and App Group IPC. Add docs/ GitHub Pages site with en/zh privacy policy for App Store compliance.
71 lines
2.0 KiB
Swift
71 lines
2.0 KiB
Swift
// HostAppLauncher.swift
|
|
// OSGKeyboard · Keyboard Extension
|
|
//
|
|
// Opens the host app via URL using every extension-safe strategy:
|
|
// 1. `extensionContext.open` (official)
|
|
// 2. Responder-chain `UIApplication.open` (TypeWhisper pattern)
|
|
// 3. `sharedApplication` KVC fallback (common in full-access keyboards)
|
|
|
|
import UIKit
|
|
|
|
enum HostAppLauncher {
|
|
@MainActor
|
|
static func open(
|
|
url: URL,
|
|
from controller: KeyboardViewController,
|
|
completion: @escaping @MainActor (Bool) -> Void
|
|
) {
|
|
if let context = controller.extensionContext {
|
|
context.open(url) { success in
|
|
Task { @MainActor in
|
|
if success {
|
|
completion(true)
|
|
return
|
|
}
|
|
completion(openViaFallback(url, from: controller))
|
|
}
|
|
}
|
|
return
|
|
}
|
|
completion(openViaFallback(url, from: controller))
|
|
}
|
|
|
|
@MainActor
|
|
private static func openViaFallback(
|
|
_ url: URL,
|
|
from controller: KeyboardViewController
|
|
) -> Bool {
|
|
if openViaResponderChain(url, from: controller) {
|
|
return true
|
|
}
|
|
return openViaSharedApplication(url)
|
|
}
|
|
|
|
@MainActor
|
|
private static func openViaResponderChain(
|
|
_ url: URL,
|
|
from controller: KeyboardViewController
|
|
) -> Bool {
|
|
var responder: UIResponder? = controller
|
|
while let current = responder {
|
|
if let application = current as? UIApplication {
|
|
application.open(url, options: [:]) { _ in }
|
|
return true
|
|
}
|
|
responder = current.next
|
|
}
|
|
return false
|
|
}
|
|
|
|
@MainActor
|
|
private static func openViaSharedApplication(_ url: URL) -> Bool {
|
|
guard
|
|
let application = UIApplication.value(forKeyPath: "sharedApplication") as? UIApplication
|
|
else {
|
|
return false
|
|
}
|
|
application.open(url, options: [:]) { _ in }
|
|
return true
|
|
}
|
|
}
|