Skip to content

Commit 22f4558

Browse files
committed
Deprecate the TextReplacement view
1 parent 31fa5eb commit 22f4558

File tree

6 files changed

+116
-48
lines changed

6 files changed

+116
-48
lines changed

Diff for: RELEASE_NOTES.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ Thanks to [pnewell](https://github.com/pnewell), SwiftUIKit now supports Catalys
1212

1313
* The `FetchedDataView` has a new view-builder initializer.
1414

15+
### 📦 New Package
16+
17+
* The `TextReplacement` has been deprecated and moved to a new package.
18+
* Check out the `https://github.com/danielsaidi/TextReplacementView` repo.
19+
1520
### 🗑️ Deprecations
1621

1722
* The `CornerRadiusStyle` type has been deprecated.
@@ -23,7 +28,6 @@ Thanks to [pnewell](https://github.com/pnewell), SwiftUIKit now supports Catalys
2328
* The `View` `any()` extension should NOT be used and has been deprecated.
2429

2530

26-
2731
## 5.2
2832

2933
This version updates the icon, logo and build scripts.

Diff for: Sources/SwiftUIKit/Buttons/ButtonType.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SwiftUIKit
44
//
55
// Created by Daniel Saidi on 2024-04-30.
6-
// Copyright © 2024-2025 Daniel Daniel Saidi. All rights reserved.
6+
// Copyright © 2024-2025 Daniel Saidi. All rights reserved.
77
//
88

99
import SwiftUI

Diff for: Sources/SwiftUIKit/Lists/ListButtonGroup.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SwiftUIKit
44
//
55
// Created by Daniel Saidi on 2024-03-15.
6-
// Copyright © 2024-2025 Daniel Daniel Saidi. All rights reserved.
6+
// Copyright © 2024-2025 Daniel Saidi. All rights reserved.
77
//
88

99
#if os(iOS)

Diff for: Sources/SwiftUIKit/Views/TextReplacement.swift

-44
This file was deleted.

Diff for: Sources/SwiftUIKit/_Deprecated/Button+Init.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SwiftUIKit
44
//
55
// Created by Daniel Saidi on 2024-04-30.
6-
// Copyright © 2024-2025 Daniel Daniel Saidi. All rights reserved.
6+
// Copyright © 2024-2025 Daniel Saidi. All rights reserved.
77
//
88

99
import SwiftUI

Diff for: Sources/SwiftUIKit/_Deprecated/TextReplacement.swift

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import SwiftUI
2+
3+
@available(*, deprecated, message: "This view has been moved to the https://github.com/danielsaidi/TextReplacementView repository.")
4+
public struct TextReplacement: View {
5+
6+
/// Create a replacement view with a single replacement.
7+
public init(
8+
_ text: String,
9+
replace: String,
10+
with replacement: @escaping (String) -> Text
11+
) {
12+
self.init(text, replacements: [replace: replacement])
13+
}
14+
15+
/// Create a replacement view with multiple replacements.
16+
public init(
17+
_ text: String,
18+
replacements: [String: (String) -> Text]
19+
) {
20+
self.content = TextReplacement.processReplacements(
21+
in: text,
22+
with: replacements
23+
)
24+
}
25+
26+
private let content: Text
27+
28+
public var body: some View {
29+
content
30+
}
31+
32+
/// Process the replacements in a deterministic way
33+
private static func processReplacements(
34+
in text: String,
35+
with replacements: [String: (String) -> Text]
36+
) -> Text {
37+
38+
// Create a structure to track replacement positions
39+
struct Replacement {
40+
let range: Range<String.Index>
41+
let pattern: String
42+
let replacementFunc: (String) -> Text
43+
}
44+
45+
// Find all occurrences of all patterns
46+
var allReplacements: [Replacement] = []
47+
48+
// Find text ranges for all specified replacements
49+
for (pattern, replacementFunc) in replacements {
50+
var searchRange = text.startIndex..<text.endIndex
51+
52+
while let range = text.range(of: pattern, range: searchRange) {
53+
allReplacements.append(Replacement(
54+
range: range,
55+
pattern: pattern,
56+
replacementFunc: replacementFunc
57+
))
58+
searchRange = range.upperBound..<text.endIndex
59+
}
60+
}
61+
62+
// Sort replacements by position, then by length
63+
// Longer patterns are handled first to handle overlaps
64+
allReplacements.sort { first, second in
65+
if first.range.lowerBound != second.range.lowerBound {
66+
return first.range.lowerBound < second.range.lowerBound
67+
}
68+
return first.pattern.count > second.pattern.count
69+
}
70+
71+
// Process the text with non-overlapping replacements
72+
var result = Text("")
73+
var currentIndex = text.startIndex
74+
75+
// Remove overlapping replacements
76+
var validReplacements: [Replacement] = []
77+
var lastEnd: String.Index?
78+
79+
for replacement in allReplacements {
80+
if let lastEnd = lastEnd, replacement.range.lowerBound < lastEnd {
81+
continue // Skip overlapping replacement
82+
}
83+
validReplacements.append(replacement)
84+
lastEnd = replacement.range.upperBound
85+
}
86+
87+
// Apply the valid replacements
88+
for replacement in validReplacements {
89+
// Add text before the replacement
90+
if currentIndex < replacement.range.lowerBound {
91+
let beforeText = text[currentIndex..<replacement.range.lowerBound]
92+
result = result + Text(String(beforeText))
93+
}
94+
95+
// Add the replacement
96+
result = result + replacement.replacementFunc(replacement.pattern)
97+
currentIndex = replacement.range.upperBound
98+
}
99+
100+
// Add any remaining text
101+
if currentIndex < text.endIndex {
102+
let remainingText = text[currentIndex..<text.endIndex]
103+
result = result + Text(String(remainingText))
104+
}
105+
106+
return result
107+
}
108+
}

0 commit comments

Comments
 (0)