Let’s see how to replace multiple tokens in string. As an example problem to solve we will use this string: The quick <color_1> <animal_1> jumps over the lazy <animal_2>
String extension for replacing tokens
Token’s format is < text _ numbers > what can be turned into regular expression: <[:alpha:]+_{1}[:digit:]+>.
We’ll extend string and add a function what takes in regular expression and closure responsible of providing replacement strings. For finding tokens, we’ll use NSRegularExpression and get all the matches in the string. Next step is to reverse enumerate matches and replace tokens. Reverse enumerating is required it ensures that token ranges are constant. If we would start replacing from the first match, then all the succeeding ranges should be shifted based on the length difference of all the preceding tokens and replacements. In this case reduce is convenient because we can enumerate all the matches and then mutating the copy of the initial string with very few lines. Another aspect to note is that NSRegularExpression uses NSRange instead of <a rel="noreferrer noopener" aria-label="RangeRange<String.Index>. Therefore we need to convert ranges from one type to another making sure character indexes match. This function can now be used with custom logic when providing replacements. For example: we can have a simple mapping or even returning the same replacement string.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
String extension replacing tokens matching a pattern.
Summary
When we would like to do multiple replacements in a string, then one of the approaches is to get all the replacement ranges and then reverse enumerating the ranges and making replacements. In this way we can avoid having complex code trying to adjust based on the length difference of the source and replacement string.
Property wrappers allow property declaration to state what kind of property wrapper is used for implementing the property. We can use it for implementing transformations on properties like validating if string is email or not. This is what we will do: creating a property wrapper for email properties and validating emails using NSDataDetector. If value being set is email, we store it and if it is not, we set the property to nil instead.
Creating property wrapper
Property wrappers are types annotated with @propertyWrapper. The type needs to implement one property: wrappedValue. Emails are represented with strings, therefore our wrappedValue property is optional string. Optional is required, because string can contain invalid email and in that case we set the property to nil. Whenever we would like to use this property wrapper, we just need to add @EmailValidated in front of the property definition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating emails using regular expressions is not easy. Fortunately Apple provides API exactly for this: NSDataDetector. We can create an instance of NSDataDetector with specifying link as detected types. When matching emails, we use anchored option as we expect the string to only include email, nothing else. Anchored will tell the data detector to match starting with the first character. As firstMatch(in:options:range:) uses NSRange, we need to convert Swift’s range to NSRange because those ranges do not have one-to-one match. For this, we can use special NSRange initialiser taking Swift string and its range.
NSDataDetector represents links with URL, therefore we will see if match contains an URL and if URL’s scheme is mailto. If it is, we can extract the matched email and return it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
For using the created property wrapper, all we need to do is to annotate property with @EmailValidated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
We created a simple property wrapper for validating emails. We saw that creating a property wrapper for validating email with NSDataDetector requires only a little bit of code.