Skip to content

Commit

Permalink
Merge pull request #1373 from wordpress-mobile/fix/ios17-crash
Browse files Browse the repository at this point in the history
Use NSString.paragraphRange method due to iOS 17 crash
  • Loading branch information
twstokes authored Sep 13, 2023
2 parents 4d0522d + 0087573 commit 512fc84
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 20 deletions.
30 changes: 11 additions & 19 deletions Aztec/Classes/Extensions/NSAttributedString+ParagraphRange.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,13 @@ extension NSAttributedString {
///
func paragraphRanges(intersecting range: NSRange, includeParagraphSeparator: Bool = true) -> [NSRange] {
var paragraphRanges = [NSRange]()
let swiftRange = string.range(fromUTF16NSRange: range)
let paragraphsRange = string.paragraphRange(for: swiftRange)

string.enumerateSubstrings(in: paragraphsRange, options: .byParagraphs) { [unowned self] (substring, substringRange, enclosingRange, stop) in
let paragraphsRange = foundationString.paragraphRange(for: range)

foundationString.enumerateSubstrings(in: paragraphsRange, options: .byParagraphs) { (substring, substringRange, enclosingRange, stop) in
let paragraphRange = includeParagraphSeparator ? enclosingRange : substringRange
paragraphRanges.append(self.string.utf16NSRange(from: paragraphRange))
paragraphRanges.append(paragraphRange)
}

return paragraphRanges
}

Expand All @@ -44,16 +43,12 @@ extension NSAttributedString {
///
func paragraphRanges(intersecting range: NSRange) -> ([ParagraphRange]) {
var paragraphRanges = [ParagraphRange]()
let swiftRange = string.range(fromUTF16NSRange: range)
let paragraphsRange = string.paragraphRange(for: swiftRange)

string.enumerateSubstrings(in: paragraphsRange, options: .byParagraphs) { [unowned self] (substring, substringRange, enclosingRange, stop) in
let substringNSRange = self.string.utf16NSRange(from: substringRange)
let enclosingNSRange = self.string.utf16NSRange(from: enclosingRange)

paragraphRanges.append((substringNSRange, enclosingNSRange))
let paragraphsRange = foundationString.paragraphRange(for: range)

foundationString.enumerateSubstrings(in: paragraphsRange, options: .byParagraphs) { (substring, substringRange, enclosingRange, stop) in
paragraphRanges.append((substringRange, enclosingRange))
}

return paragraphRanges
}

Expand All @@ -62,10 +57,7 @@ extension NSAttributedString {
/// This is an attributed string wrapper for `NSString.paragraphRangeForRange()`
///
func paragraphRange(for range: NSRange) -> NSRange {
let swiftRange = string.range(fromUTF16NSRange: range)
let outRange = string.paragraphRange(for: swiftRange)

return string.utf16NSRange(from: outRange)
return foundationString.paragraphRange(for: range)
}

func paragraphRange(for attachment: NSTextAttachment) -> NSRange {
Expand Down
50 changes: 50 additions & 0 deletions AztecTests/Extensions/NSAttributedStringParagraphRangeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,54 @@ class NSAttributedStringParagraphRangeTests: XCTestCase {
XCTAssertEqual(ranges.first?.rangeExcludingParagraphSeparator, expectedRangeWithoutSeparator)
XCTAssertEqual(ranges.first?.rangeIncludingParagraphSeparator, expectedRangeWithSeparator)
}

/// Tests that `paragraphRange(for:)` with a paragraph separator character
/// returns the correct `NSRange`.
///
/// This test was added due to an iOS 17 crash when calling String.paragraphRange(for: range)
/// on a single paragraph separator character.
///
func testParagraphRangeWorkWithParagraphSeparator() {
let attributedString = NSAttributedString(string: "\u{2029}")
let range = NSRange(location: 0, length: 1)
let expectedRange = NSRange(location: 0, length: 1)

let paragraphRange = attributedString.paragraphRange(for: range)

XCTAssertEqual(paragraphRange, expectedRange)
}

/// Tests that `paragraphRanges(intersecting:)` with a paragraph separator character
/// returns the correct `[NSRange]`.
///
/// This test was added due to an iOS 17 crash when calling String.paragraphRange(for: range)
/// on a single paragraph separator character.
///
func testParagraphRangesWorkWithParagraphSeparator() {
let attributedString = NSAttributedString(string: "\u{2029}")
let range = NSRange(location: 0, length: 1)
let expectedRange = NSRange(location: 0, length: 1)

let ranges = attributedString.paragraphRanges(intersecting: range, includeParagraphSeparator: true)

XCTAssertEqual(ranges.first!, expectedRange)
}

/// Tests that `paragraphRanges(intersecting:)` with a paragraph separator character
/// returns the correct `ParagraphRange`.
///
/// This test was added due to an iOS 17 crash when calling String.paragraphRange(for: range)
/// on a single paragraph separator character.
///
func testParagraphRangesWorkWithAndWithoutParagraphSeparator() {
let attributedString = NSAttributedString(string: "\u{2029}")
let range = NSRange(location: 0, length: 1)
let expectedRangeWithoutSeparator = NSRange(location: 0, length: 0)
let expectedRangeWithSeparator = NSRange(location: 0, length: 1)

let ranges = attributedString.paragraphRanges(intersecting: range)

XCTAssertEqual(ranges.first!.rangeIncludingParagraphSeparator, expectedRangeWithSeparator)
XCTAssertEqual(ranges.first!.rangeExcludingParagraphSeparator, expectedRangeWithoutSeparator)
}
}
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ _None._

### Bug Fixes

_None._
* Worked around a crash that could occur when calling String.paragraphRange(for:) on iOS 17. [#1373]

### Internal Changes

Expand Down

0 comments on commit 512fc84

Please sign in to comment.