Overview
crash stack
#0 at 0x1d910a3a4
Thread 0 Crashed
0 libsystem_kernel.dylib nil
1 libsystem_kernel.dylib nil
2 libsystem_c.dylib _os_crash_msg
3 UIKitCore _UIResponderForwarderWantsForwardingFromResponder
4 UIKitCore __forwardTouchMethod_block_invoke
5 CoreFoundation __NSSET_IS_CALLING_OUT_TO_A_BLOCK__
6 CoreFoundation -[__NSSetM enumerateObjectsWithOptions:usingBlock:]
7 UIKitCore forwardTouchMethod
8 UIKitCore -[UIWindow _sendTouchesForEvent:]
9 UIKitCore -[UIWindow sendEvent:]
10 UIKitCore -[UIApplication sendEvent:]
11 Eax-AppStore EaxApplication.sendEvent(UIEvent)(Application.swift:16)
11 Eax-AppStore @objc EaxApplication.sendEvent(UIEvent)(<compiler-generated>:14)
12 UIKitCore __dispatchPreprocessedEventFromEventQueue
13 UIKitCore __processEventQueue
14 UIKitCore updateCycleEntry
15 UIKitCore _UIUpdateSequenceRun
16 UIKitCore schedulerStepScheduledMainSection
17 UIKitCore runloopSourceCallback
18 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
19 CoreFoundation __CFRunLoopDoSource0
20 CoreFoundation __CFRunLoopDoSources0
21 CoreFoundation __CFRunLoopRun
22 CoreFoundation CFRunLoopRunSpecific
23 GraphicsServices GSEventRunModal
24 UIKitCore -[UIApplication _run]
25 UIKitCore UIApplicationMain
26 Eax-AppStore main(main.swift:11)
27 unknown nil
scenario: page that contains keyboard
os version: 16.5.1
[[Application.swift:16 EaxApplication.sendEvent(UIEvent)]]


Clue
System keyboard: no crash
Third party keyboard: crash

- For iOS >= 9 third party keyboard will visible on UIRemoteKeyboardWindow window (UIRemoteKeyboardWindow is above all others window)

-
touch event is sent to **UIRemoteKeyboardWindow **when third party keyboard just raise. Crash will happen when (multiple?) event sent to UIRemoteKeyboardWindow.
-
After keyboard raised, touch event is sent to **UIRemoteKeyboardWindow **without crash

Recap: Crash happen only when third party keyboard receive (multiple?) event when it is about raise.
Suspect
UIRemoteKeyboardWindowreceive event when keyboard is about raise
Solution
- Block **UIRemoteKeyboardWindow **event chain
class EaxApplication: UIApplication {
override func sendEvent(_ event: UIEvent) {
if EaxABTestKey.shared.ENABLE_KEYBOARD_PROTECTION.isB() {
guard canResponseToEvent(event) else { return }
}
sendNotificationToTTIModuleWhenFirstTouchIsFired(event)
super.sendEvent(event)
}
override func sendEvent(_ event: UIEvent) {
if EaxABTestKey.shared.ENABLE_KEYBOARD_PROTECTION.isB() {
guard canResponseToEvent(event) else { return }
}
sendNotificationToTTIModuleWhenFirstTouchIsFired(event)
super.sendEvent(event)
}
private func sendNotificationToTTIModuleWhenFirstTouchIsFired(_ event: UIEvent) {
guard TTILogCore.defaultCenter.activated, event.type == .touches else { return }
TTILogCenter.submitLog()
}
func canResponseToEvent(_ event: UIEvent) -> Bool {
if UIDevice.current.systemVersion == "16.5.1" {
if event.type == .touches {
let allTouches: [UITouch] = Array(event.allTouches ?? [])
for touch in allTouches {
let windowName = String(describing: touch.window.self)
if windowName.contains("UIRemoteKeyboardWindow") {
guard canResponseToWindow(touch.window) else { return false }
}
}
}
}
self.lastResponseDate = Date()
return true
}
// Block UIRemoteKeyboardWindow event chain
func canResponseToWindow(_ window: UIWindow?) -> Bool {
guard Date().timeIntervalSince(lastResponseDate) >= timerInterval else {
return false
}
return true
}
}
- Block with timer interval for UIRemoteKeyboardWindow
class EaxApplication: UIApplication {
override func sendEvent(_ event: UIEvent) {
if EaxABTestKey.shared.ENABLE_KEYBOARD_PROTECTION.isB() {
guard canResponseToEvent(event) else { return }
}
sendNotificationToTTIModuleWhenFirstTouchIsFired(event)
super.sendEvent(event)
}
override func sendEvent(_ event: UIEvent) {
if EaxABTestKey.shared.ENABLE_KEYBOARD_PROTECTION.isB() {
guard canResponseToEvent(event) else { return }
}
sendNotificationToTTIModuleWhenFirstTouchIsFired(event)
super.sendEvent(event)
}
private func sendNotificationToTTIModuleWhenFirstTouchIsFired(_ event: UIEvent) {
guard TTILogCore.defaultCenter.activated, event.type == .touches else { return }
TTILogCenter.submitLog()
}
func canResponseToEvent(_ event: UIEvent) -> Bool {
if UIDevice.current.systemVersion == "16.5.1" {
if event.type == .touches {
let allTouches: [UITouch] = Array(event.allTouches ?? [])
for touch in allTouches {
let windowName = String(describing: touch.window.self)
if windowName.contains("UIRemoteKeyboardWindow") {
guard canResponseToWindow(touch.window) else { return false }
}
}
}
}
self.lastResponseDate = Date()
return true
}
// Block with timer interval for UIRemoteKeyboardWindow
func canResponseToWindow(_ window: UIWindow?) -> Bool {
if let currentWindow = currentWindow,
currentWindow == window {
return true
} else {
// key point: block event for UIRemoteKeyboardWindow for a period time
currentWindow = window
guard Date().timeIntervalSince(lastResponseDate) >= timerInterval else {
return false
}
return true
}
}
}
UIRemoteKeyboardWindow Response Arear


When a new event is ready, we should check does this event belongs to UIRemoteKeyboardWindow and time interval is too shot to response.

Increase the time interval for different UIRemoteKeyboardWindow instance calls
网友评论