Reputation: 69
Any better way to retrieve the Range after string replace?
Here is the current version:
let username = "apple"
msgString = "{{user}} has an apple"
guard let range = msgString.range(of: "{{user}}") else { return }
msgString.replaceSubrange(range, with username)
userRange = msgString.range(of: username)
Does any better way to get the user name range
Upvotes: 2
Views: 4391
Reputation: 535304
Let's start with a corrected version of the code you gave:
let username = "apple"
var msgString = "{{user}} has an apple"
guard let range = msgString.range(of: "{{user}}") else { return }
msgString.replaceSubrange(range, with: username)
Here, within msgString
we have replaced "{{user}}"
with "apple"
. The question is: what is the range of this "apple"
within msgString
?
The solution is simple when you realize that we already know the answer. It comes in two parts:
The new range starts in the same place the old range did. That is the same as saying that the lower bound of the new range is the same as the lower bound of the old range. We already know what that is: it's range.lowerBound
.
The new range's length is the length of the replacement string. This is expressed its count
.
Hence the desired range is:
let newRange =
range.lowerBound..<msgString.index(range.lowerBound, offsetBy: username.count)
If we're going to be doing a lot of this sort of thing, it might be nice to extend String. For example:
extension String {
mutating func replaceSubrangeAndReportNewRange (
_ bounds: Range<String.Index>, with s: String
) -> Range<String.Index> {
self.replaceSubrange(bounds, with:s)
return bounds.lowerBound..<self.index(bounds.lowerBound, offsetBy:s.count)
}
}
Upvotes: 1
Reputation: 119340
Two functions:
replacingOccurrences...
returns a new string and informs about the change rangesreplaceOccurrences...
changes the value of the current variable and informs about the change rangesThe best place to implement those is in String
extension:
extension String {
func replacingOccurrences<T: StringProtocol>(of toReplace: T,
with newString: T,
options: String.CompareOptions = [],
range searchRange: Range<T.Index>? = nil,
completion: ((Range<T.Index>?, Range<T.Index>?) -> Void)) -> String {
let oldRange = range(of: toReplace)
let replacedString = replacingOccurrences(of: toReplace,
with: newString,
options: options,
range: searchRange)
let newRange = replacedString.range(of: newString)
completion(oldRange, newRange)
return replacedString
}
mutating func replaceOccurrences<T: StringProtocol>(of toReplace: T,
with newString: T,
options: String.CompareOptions = [],
range searchRange: Range<T.Index>? = nil,
completion: ((Range<T.Index>?, Range<T.Index>?) -> Void)) {
self = replacingOccurrences(of: toReplace,
with: newString,
options: options,
range: searchRange,
completion: completion)
}
}
Usage:
let username = "apple"
var msgString = "{{user}} has an apple"
msgString.replaceOccurrences(of: "{{user}}", with: username) { (oldRange, newRange) in
print(oldRange)
print(newRange)
}
Upvotes: 1
Reputation: 1734
i suggest you to create a class for doing stuff that you do multiple times
in this class you can get stringRange and newReplacedString with replace method :
class StringService {
/** create an object to access every method because this class provides you services */
static let shared = StringService()
/** Service classes should not be available for creating instances */
private init () {}
func replace(mySting: String ,by: String, with: String) -> (String,Range<String.Index>?) {
var newString = ""
if let range = myString.range(of:by) {
newString.replaceSubrange(range,with:with)
return (newString,range)
}esle{
return ("",nil)
}
}
let replacedString = StringService.shared.replace(mySting: "myString is",
by: "is",
with: "was").0
if let replacedStringRange = StringService.shared.replace(mySting: "myString is",
by: "is",
with: "was").1 {
}
if you wanted to use native library you can use :
"myString is".replacingOccurrences(of: "is",
with: "was",
options: .literal,
range: nil)
Upvotes: 1
Reputation: 6213
Swift 4.0
You can use replacingOccurrences
method to replace the text.
msgString.replacingOccurrences(of: "{{user}}", with: "apple", options: .literal, range: nil)
output:
apple has an apple
Upvotes: -1