Skip to content

Commit afd0809

Browse files
authored
fix: native alert crash (#178)
## 📜 Description Fixed a crash on iOS 16.5 when native alert is getting shown. ## 💡 Motivation and Context The original exception occurred because of this error: ```bash libc++abi: terminating due to uncaught exception of type folly::json::print_error: folly::toJson: JSON object value was an INF when serializing value at "progress" ``` `progress` was `inf` because in: ```swift onEvent("onKeyboardMove", keyboardPosition as NSNumber, keyboardPosition / CGFloat(keyboardHeight) as NSNumber) ``` `keyboardHeight` variable was `0`. When we were trying to divide any number by `0` in swfit we get `Inf` value, and this value can not be serialised to JS equivalent. The reason why this value becomes `0` is the fact that we have next chain of calls: ``` keyboardWillHide keyboardDidHide <- here we are setting keyboardHeight=0 keyboardWillHide <- here we setup CADisplayLink and after 16ms the crash will occur keyboardDidHide ``` Based on output from above I had 3 options on how to fix the error: ### 1. Add `.isFinite` check Good option, but it will prevent sending `onMove` event, though according to values from `CADisplayLink` movement actually happens. So we will kind of ignore actual information and it's not something that satisfy me, because it may produce other bugs in the future. ### 2. Update `keyboardHeight` in `keyboardWillHide` This variant also prevents a crash, but `progress` value in `onMove` sometimes can be higher than `1` (in my tests sometimes it was 1.3+). And it also seems like it gives incorrect calculation, because it's strange to see progress > 1, when keyboard simply hides 🤷‍♂️ ### 3. Don't set `keyboardHeight` to `0` To come back to `1.4.x` behaviour, we don't need to reset `keyboardHeight` to `0`. But I've added this code intentionally in `1.5.0`, because in KV observer we are checking that keyboard should be open and not animating: ```swift if displayLink != nil || keyboardHeight == 0.0 { return } ``` I've added this check, because I've attached KV observer before any keyboard movement. The reason why I've added it in `UIWindow.didBecomeVisibleNotification`/`UIWindow.didBecomeHiddenNotification` was the fact, that these methods were called only one time (keyboard events can be fired multiple times). But I've added `hasKVObserver` variable in #146 so it's safe to call `setupKVObserver`/`removeKVObserver` multiple times. So in this PR I've decided to stick to this approach - don't reset `keyboardHeight` to `0` + setup KV listeners in different lifecycle methods. Should close #175 ## 📢 Changelog ### iOS - moved `setupKVObserver`/`removeKVObserver` to `keyboaardDidAppear`/`keyboardWilHide` lifecycle; - don't set `keyboardHeight` to `0` (this value acts as `persisteKeyboardHeight` on Android); ## 🤔 How Has This Been Tested? Tested on: - iPhone 6s (iOS 15.6); - iPhone 14 Pro (iOS 16.2); - iPhone 11 (iOS 16.5); ## 📸 Screenshots (if appropriate): |Before|After| |------|-----| |<video src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/82d46951-3471-4b59-8dd6-3062922a76f0">|<video src="https://github.com/kirillzyusko/react-native-keyboard-controller/assets/22820318/b73d2aa2-72a9-4272-8b37-f6820d1c816a">| ## 📝 Checklist - [x] CI successfully passed
1 parent ae73a9c commit afd0809

File tree

1 file changed

+4
-24
lines changed

1 file changed

+4
-24
lines changed

Diff for: ios/KeyboardMovementObserver.swift

+4-24
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,6 @@ public class KeyboardMovementObserver: NSObject {
6565
name: UIResponder.keyboardDidHideNotification,
6666
object: nil
6767
)
68-
NotificationCenter.default.addObserver(
69-
self,
70-
selector: #selector(windowDidBecomeVisible),
71-
name: UIWindow.didBecomeVisibleNotification,
72-
object: nil
73-
)
74-
NotificationCenter.default.addObserver(
75-
self,
76-
selector: #selector(windowDidBecomeHidden),
77-
name: UIWindow.didBecomeHiddenNotification,
78-
object: nil
79-
)
80-
}
81-
82-
@objc func windowDidBecomeHidden(_: Notification) {
83-
removeKVObserver()
84-
}
85-
86-
@objc func windowDidBecomeVisible(_: Notification) {
87-
setupKVObserver()
8868
}
8969

9070
private func setupKVObserver() {
@@ -116,8 +96,8 @@ public class KeyboardMovementObserver: NSObject {
11696
) {
11797
// swiftlint:disable:next force_cast
11898
if keyPath == "center", object as! NSObject == _keyboardView! {
119-
// if we are currently animating keyboard or keyboard is not shown yet -> we need to ignore values from KVO
120-
if displayLink != nil || keyboardHeight == 0.0 {
99+
// if we are currently animating keyboard -> we need to ignore values from KVO
100+
if displayLink != nil {
121101
return
122102
}
123103

@@ -170,6 +150,7 @@ public class KeyboardMovementObserver: NSObject {
170150
onNotify("KeyboardController::keyboardWillHide", data)
171151

172152
setupKeyboardWatcher()
153+
removeKVObserver()
173154
}
174155

175156
@objc func keyboardDidAppear(_ notification: Notification) {
@@ -184,15 +165,14 @@ public class KeyboardMovementObserver: NSObject {
184165
onNotify("KeyboardController::keyboardDidShow", data)
185166

186167
removeKeyboardWatcher()
168+
setupKVObserver()
187169
}
188170
}
189171

190172
@objc func keyboardDidDisappear() {
191173
var data = [AnyHashable: Any]()
192174
data["height"] = 0
193175

194-
keyboardHeight = 0.0
195-
196176
onEvent("onKeyboardMoveEnd", 0 as NSNumber, 0)
197177
onNotify("KeyboardController::keyboardDidHide", data)
198178

0 commit comments

Comments
 (0)