Skip to content

Commit

Permalink
[Link] Fix OneTimeTextField crash in Mac Catalyst (#1416)
Browse files Browse the repository at this point in the history
* Fix OneTimeTextField crash in Mac Catalyst

* Update README

* Fix rect calculation

* Cleanup

* Fix and document implementation
ramont-stripe authored Sep 8, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 455b881 commit 673d9e0
Showing 3 changed files with 59 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## X.Y.Z
### PaymentSheet
* [Fixed] Fixed potential crash when using Link in Mac Catalyst.

## 22.8.0 2022-09-06
### PaymentSheet
* [Changed] Renamed `PaymentSheet.reset()` to `PaymentSheet.resetCustomer()`. See `MIGRATING.md` for more info.
30 changes: 25 additions & 5 deletions Stripe/OneTimeCodeTextField.swift
Original file line number Diff line number Diff line change
@@ -360,6 +360,16 @@ extension OneTimeCodeTextField: UIKeyInput {

}

// MARK: - Utils

extension OneTimeCodeTextField {

private func clampIndex(_ index: Int) -> Int {
return max(min(index, numberOfDigits - 1), 0)
}

}

// MARK: - UITextInput

extension OneTimeCodeTextField: UITextInput {
@@ -517,15 +527,25 @@ extension OneTimeCodeTextField: UITextInput {
}

func firstRect(for range: UITextRange) -> CGRect {
guard let range = range as? TextRange else {
guard let range = range as? TextRange, !range.isEmpty else {
return .zero
}

let firstDigitView = digitViews[range._start.index]
let secondDigitView = digitViews[range._end.index]
// This method should return a rectangle that contains the digit views that
// fall inside the given TextRange. For example, a [0,2] TextRange should
// return a rectangle that contains digit views 0 and 1:
//
// 0 1 2 3 4 5 6 <- TextPosition
// [*] [*] [*] [*] [*] [*] <- UI
// 0 1 2 3 4 5 <- DigitView index
// ^ ^
// |_______| <- [0,2] TextRange

let firstDigitView = digitViews[clampIndex(range._start.index)]
let secondDigitView = digitViews[clampIndex(range._end.index - 1)]

let firstRect = firstDigitView.convert(firstDigitView.bounds, to: self)
let secondRect = firstDigitView.convert(secondDigitView.bounds, to: self)
let secondRect = secondDigitView.convert(secondDigitView.bounds, to: self)

return firstRect.union(secondRect)
}
@@ -535,7 +555,7 @@ extension OneTimeCodeTextField: UITextInput {
return .zero
}

let digitView = digitViews[position.index]
let digitView = digitViews[clampIndex(position.index)]
return digitView.convert(digitView.caretRect, to: self)
}

30 changes: 30 additions & 0 deletions Tests/Tests/OneTimeCodeTextFieldTests.swift
Original file line number Diff line number Diff line change
@@ -236,6 +236,36 @@ class OneTimeCodeTextFieldTests: XCTestCase {
XCTAssertNil(field.characterRange(byExtending: position, in: .down))
}

func test_firstRectForRange_singleDigit() {
let sut = makeSUT(value: "123456")

// A [0,1] text range
let range = OneTimeCodeTextField.TextRange(
start: OneTimeCodeTextField.TextPosition(0),
end: OneTimeCodeTextField.TextPosition(1)
)
let rect = sut.firstRect(for: range)
XCTAssertEqual(rect.minX, 0, accuracy: 0.2)
XCTAssertEqual(rect.minY, 0, accuracy: 0.2)
XCTAssertEqual(rect.width, 46.0, accuracy: 0.2)
XCTAssertEqual(rect.height, 60, accuracy: 0.2)
}

func test_firstRectForRange_multipleDigits() {
let sut = makeSUT(value: "123456")

// A [0,3] Text range
let range = OneTimeCodeTextField.TextRange(
start: OneTimeCodeTextField.TextPosition(0),
end: OneTimeCodeTextField.TextPosition(3)
)
let rect = sut.firstRect(for: range)
XCTAssertEqual(rect.minX, 0, accuracy: 0.2)
XCTAssertEqual(rect.minY, 0, accuracy: 0.2)
XCTAssertEqual(rect.width, 150, accuracy: 0.2)
XCTAssertEqual(rect.height, 60, accuracy: 0.2)
}

func test_caretRectForPosition() {
let sut = makeSUT()
let frame = sut.caretRect(for: OneTimeCodeTextField.TextPosition(1))

0 comments on commit 673d9e0

Please sign in to comment.