[docs] Extensions.md: Strings & Text section

This commit is contained in:
Daniel Arantes Loverde
2026-08-29 19:25:15 -03:00
parent 9f1db7c121
commit 1d2c595c78

View File

@@ -119,7 +119,560 @@ await API.shared.setupCertification(certData: p12, password: "cert-pw")
## Strings & Text
<!-- batch 3 -->
<details>
<summary><b>String</b> — validation, parsing, formatting, masks, HTML, dates</summary>
### URLs
#### `var isValidUrl: Bool` / `var isValidHttpsUrl: Bool` / `var isValidHttpUrl: Bool`
`isValidUrl` is true when `URL(string:)` succeeds; the other two additionally
require the `https` / `http` scheme.
```swift
"https://google.com".isValidUrl // true
"https://google.com".isValidHttpsUrl // true
"http://google.com".isValidHttpsUrl // false
```
#### `var urlEncoded: String` / `var urlDecoded: String`
Percent-encode (host-allowed set) / decode. `urlDecoded` returns the original
string when it is not encoded.
```swift
"it's easy".urlEncoded // "it's%20easy"
"it's%20easy".urlDecoded // "it's easy"
```
#### `mutating func urlEncode() -> String` / `mutating func urlDecode() -> String`
In-place variants; also return the new value (`@discardableResult`).
```swift
var s = "a b"; s.urlEncode() // s == "a%20b"
```
#### `func stringByAddingPercentEncodingForRFC3986() -> String`
Percent-encodes for use as a query key/value, escaping `;/?:@&=+$,` and space.
```swift
"a&b=c".stringByAddingPercentEncodingForRFC3986() // "a%26b%3Dc"
```
#### `var url: String?`
First URL detected **inside** the string (via `NSDataDetector`), or `nil`.
```swift
"visit www.site.com.br now".url // "www.site.com.br"
```
#### `var toURL: NSURL?`
`NSURL(string:)` wrapper.
```swift
"https://x.com".toURL // NSURL
```
### Validation
#### `var isEmail: Bool`
Regex check for a syntactically valid email (TLD 220 chars).
```swift
"user@example.com".isEmail // true
"nope".isEmail // false
```
#### `var isCPF: Bool`
Validates a Brazilian CPF including both check digits. Strips formatting first
(`onlyNumbers`), requires 11 digits.
```swift
"123.456.789-09".isCPF // true when the check digits match
```
#### `var isValidCNPJ: Bool`
Validates a Brazilian CNPJ (14 digits, both check digits, rejects all-same-digit).
```swift
"11.222.333/0001-81".isValidCNPJ // true when valid
```
#### `var isAlphabetic: Bool`
Letters only, no digits.
```swift
"abc".isAlphabetic // true
"123abc".isAlphabetic // false
```
#### `var isAlphaNumeric: Bool`
Contains at least one letter **and** one digit and nothing else — handy for
password rules.
```swift
"123abc".isAlphaNumeric // true
"abc".isAlphaNumeric // false
```
#### `var isHTML: Bool`
True if the string contains an HTML tag.
```swift
"<b>hi</b>".isHTML // true
```
#### `func validateBolean(comparingBoolean: Bool = true) -> Bool`
Loose truthy/falsy check against a large set of EN/PT words. With
`comparingBoolean: true` returns whether the string means "true"
(`YES`, `ON`, `SIM`, `ATIVO`, `1`, `T`, …); with `false`, whether it means "false".
```swift
"SIM".validateBolean() // true
"nao".validateBolean(comparingBoolean: false) // true
```
### Conversion
#### `var bool: Bool?`
`"true"/"yes"/"1"``true`, `"false"/"no"/"0"``false`, else `nil` (trimmed, case-insensitive).
```swift
" YES ".bool // true
"maybe".bool // nil
```
#### `var int: Int?` / `var float: Float?` / `var double: Double?`
Plain `Int(self)` / `Float(self)` / `Double(self)`.
```swift
"101".int // 101
"1.5".double // 1.5
"x".int // nil
```
#### `func float(locale: Locale = .current) -> Float?` / `func double(locale:) -> Double?`
Locale-aware parsing via `NumberFormatter` (accepts grouping separators).
```swift
"1,5".double(locale: Locale(identifier: "pt_BR")) // 1.5
```
#### `var currencyStringToDouble: Double`
Parses a `pt_BR` currency string to `Double`, `0.0` on failure.
```swift
"R$ 1.234,56".currencyStringToDouble // 1234.56
```
#### `var btcToSats: Int` / `var bitcoinToSatoshis: Int`
Multiplies a BTC amount string by 100,000,000. `bitcoinToSatoshis` is an alias.
```swift
"0.0001".btcToSats // 10000
```
#### `var data: Data`
UTF-8 bytes.
```swift
"hi".data // 2 bytes
```
#### `var nsString: NSString` / `var fullNSRange: NSRange`
Bridge to `NSString`; `NSRange` spanning the whole string (UTF-16 aware).
```swift
"café".fullNSRange // {0, 4}
```
#### `func nsRange(from range: Range<String.Index>) -> NSRange?`
Convert a Swift `Range` to an `NSRange` in the UTF-16 view.
#### `var base64Encode: String?` / `var base64Decode: String?`
Base64 encode the UTF-8 bytes / decode a Base64 string back to text.
```swift
"hi".base64Encode // "aGk="
"aGk=".base64Decode // "hi"
```
#### `func date(withCurrFormatt:localeIdentifier:timeZone:) -> Date?`
Parse the string to `Date` using the given input format (default
`"yyyy-MM-dd HH:mm:ss"`, locale `pt-BR`, current time zone). A `" 0000"` suffix
is normalised to `" +0000"`.
```swift
"2026-08-29 14:30:00".date() // Date
```
#### `func date(withCurrFormatt:newFormatt:localeIdentifier:timeZone:) -> Date?`
Parse with one format and round-trip through another (normalises the value).
#### `var currentTimeZone: String`
Current time-zone offset string, e.g. `"-0300"`.
### Cleaning & filtering
#### `var withoutSpacesAndNewLines: String`
Removes every space and `\n`.
```swift
" a \n b ".withoutSpacesAndNewLines // "ab"
```
#### `var onlyNumbers: String` / `var numbers: String`
Digits only. `onlyNumbers` uses a `\D` regex; `numbers` uses `decimalDigits`.
```swift
"(11) 98765-4321".onlyNumbers // "11987654321"
```
#### `var letters: String` / `var lettersWithWhiteSpace: String`
Keep only letters (optionally keeping spaces).
```swift
"a1 b2".letters // "ab"
"a1 b2".lettersWithWhiteSpace // "a b"
```
#### `var alphanumeric: String` / `var alphanumericWithWhiteSpace: String`
Keep only alphanumerics (optionally keeping spaces).
```swift
"a-b_c 1".alphanumeric // "abc1"
```
#### `var removeSpecialChars: String`
Keeps `[A-Za-z0-9 -]` only.
```swift
"a@b#c".removeSpecialChars // "abc"
```
#### `var removeHTMLTags: String` / `var removeEmoji: String`
Strip HTML tags / strip emoji (`CharacterSet.symbols`).
```swift
"<p>hi</p>".removeHTMLTags // "hi"
"hi 😀".removeEmoji // "hi "
```
### Slicing & padding
#### `var first: String` / `var last: String`
First / last character **as a String** (`""` when empty).
#### `var uppercaseFirst: String`
Capitalises the first character only.
```swift
"hello".uppercaseFirst // "Hello"
```
#### `var firstCharacterAsString: String?` / `var lastCharacterAsString: String?`
Optional variants — `nil` when empty.
#### `func paddingStart(_ length: Int, with: String = " ") -> String` / `func paddingEnd(...)`
Pad to `length` with a repeating pad string at the start / end. No-op if already long enough.
```swift
"hue".paddingStart(10) // " hue"
"hue".paddingEnd(10, with: "br") // "huebrbrbrb"
```
#### `func truncated(toLength: Int, trailing: String? = "...") -> String`
Non-mutating truncation.
```swift
"This is long".truncated(toLength: 7) // "This is..."
```
#### `mutating func truncate(toLength: Int, trailing: String? = "...") -> String`
In-place truncation (`@discardableResult`).
#### `mutating func trim() -> String`
Trim leading/trailing whitespace and newlines, in place (`@discardableResult`).
```swift
var s = " hi \n"; s.trim() // s == "hi"
```
#### `mutating func reverse() -> String`
Reverse in place (`@discardableResult`).
#### `mutating func insertAtIndexEnd(string:ind:)` / `insertAtIndexStart(string:ind:)`
Insert `string` at an offset measured from `endIndex` (negative `ind` moves left).
```swift
var s = "abcd"; s.insertAtIndexEnd(string: "-", ind: -1) // "abc-d"
```
### Replacing
#### `func replace(from:to:)` / `func findAndReplace(from:to:)`
Simple substring replacement (`findAndReplace` is generic over `StringProtocol`).
```swift
"a.b.c".replace(from: ".", to: "-") // "a-b-c"
```
#### `func replacing(range: CountableClosedRange<Int>, with: String) -> String`
Replace by integer character range.
```swift
"abcdef".replacing(range: 1...3, with: "X") // "aXef"
```
#### `func replacingLastOccurrenceOfString(_:with:caseInsensitive: Bool = true) -> String`
Replace only the last match.
```swift
"a-b-c".replacingLastOccurrenceOfString("-", with: "+") // "a-b+c"
```
#### `func replaceAll(of pattern: String, with: String, options: = []) -> String`
Regex replace-all; returns the original on a bad pattern.
```swift
"a1b2c3".replaceAll(of: "[0-9]", with: "#") // "a#b#c#"
```
#### `@discardableResult func replaceURL(_ withDict: [String: Any]) -> String`
Substitute `{key}` placeholders — used by `API.request(pathParams:)`.
```swift
"/users/{id}/posts/{p}".replaceURL(["id": 7, "p": "x"]) // "/users/7/posts/x"
```
### Words & search
#### `func words() -> [String]` / `func wordCount() -> Int`
Split on whitespace + punctuation, dropping empties.
```swift
"Swift is amazing".words() // ["Swift", "is", "amazing"]
"Swift is amazing".wordCount() // 3
```
#### `func contains(_:caseSensitive: Bool = true) -> Bool`
Substring check with optional case-insensitivity.
```swift
"Hello".contains("ell") // true
"Hello".contains("HELLO", caseSensitive: false) // true
```
### Formatting helpers
#### `func applyMask(toText: String, mask: String) -> String`
Apply a `#`-placeholder mask; literal characters in the mask are inserted.
```swift
"11987654321".applyMask(toText: "11987654321", mask: "(##) #####-####")
// "(11) 98765-4321"
```
#### `func exponentize(str: String) -> String`
Turn `^`-prefixed digits into Unicode superscripts.
```swift
"x^2 + y^3".exponentize(str: "x^2 + y^3") // "x² + y³"
```
#### `func stringFromTimeInterval(_ interval: TimeInterval) -> NSString`
Format a `TimeInterval` as `HH:MM:SS.mmm`.
```swift
"".stringFromTimeInterval(3661.5) // "01:01:01.500"
```
#### `func toSlug() -> String`
Lowercase, de-accent, spaces → `-`, strip other punctuation.
```swift
"Olá Mundo!".toSlug() // "ola-mundo"
```
#### `func localized(comment: String = "") -> String`
`NSLocalizedString(self, comment:)`.
```swift
"welcome_title".localized()
```
### Generators & misc
#### `static func loremIpsum(ofLength length: Int = 445) -> String`
Lorem-ipsum text truncated to `length` (max 445).
```swift
String.loremIpsum(ofLength: 20) // "Lorem ipsum dolor si"
```
#### `func randomString(length: Int) -> String`
Random `[A-Za-z0-9]` string. (Instance method — the receiver is ignored.)
```swift
"".randomString(length: 8) // e.g. "a9Fk2Lp0"
```
#### `var JSONStringToDictionary: [String: Any]?`
Parse a JSON object string to a dictionary (`nil` + logs on failure).
```swift
#"{"a":1}"#.JSONStringToDictionary // ["a": 1]
```
#### `var convertToHTML: NSAttributedString?`
Render an HTML string to `NSAttributedString` (UIKit path uses the CSS converter below).
#### `func convertHtmlToAttributedStringWithCSS(font:csscolor:lineheight:csstextalign:customCSS:) -> NSAttributedString?` — *UIKit only*
HTML → `NSAttributedString` with an injected `<style>` block. Returns the plain
HTML rendering when `font` is `nil`.
```swift
"<b>Hi</b>".convertHtmlToAttributedStringWithCSS(
font: .systemFont(ofSize: 16), csscolor: "#333",
lineheight: 0, csstextalign: "left"
)
```
#### `func height(withConstrainedWidth:font:) -> CGFloat` / `func width(withConstraintedHeight:font:) -> CGFloat` — *UIKit only*
Measured bounding height/width for the string at a fixed width/height and font.
```swift
"Some label text".height(withConstrainedWidth: 200, font: .systemFont(ofSize: 14))
```
</details>
<details>
<summary><b>Character</b> — classification, conversion, repetition</summary>
### `var isEmoji: Bool`
True when the scalar falls in a known emoji range.
```swift
Character("😀").isEmoji // true
Character("a").isEmoji // false
```
### `var int: Int?` / `var string: String`
Digit value (or `nil`) / one-character `String`.
```swift
Character("7").int // 7
Character("A").int // nil
```
### `var lowercased: Character` / `var uppercased: Character`
Case-flipped character.
```swift
Character("a").uppercased // "A"
```
### `func unicodeScalarCodePoint() -> UInt32`
First Unicode scalar value.
```swift
Character("A").unicodeScalarCodePoint() // 65
```
### `static func randomAlphanumeric() -> Character`
Random `[A-Za-z0-9]` character.
```swift
Character.randomAlphanumeric() // e.g. "k"
```
### `static func * (Character, Int) -> String` / `static func * (Int, Character) -> String`
Repeat a character into a string.
```swift
Character("-") * 5 // "-----"
5 * Character("-") // "-----"
```
</details>
<details>
<summary><b>NSString</b></summary>
### `var string: String?`
`String(describing:)` of the `NSString`.
### `func randomAlphaNumericString(_ length: Int = 8) -> String`
Random `[A-Za-z0-9]` string of the given length.
```swift
("" as NSString).randomAlphaNumericString(12)
```
</details>
<details>
<summary><b>NSAttributedString / NSMutableAttributedString</b></summary>
### `NSAttributedString(html: String)` — *failable init*
Build an attributed string from an HTML fragment.
```swift
label.attributedText = NSAttributedString(html: "<b>Hello</b> world")
```
### `NSMutableAttributedString` builders — *UIKit only*, all `@discardableResult` and chainable
| Method | Effect |
|---|---|
| `customize(_:withFont:color:lineSpace:alignment:changeCurrentText:)` | append (or restyle) a run with font/color/spacing/alignment |
| `underline(_:withFont:color:changeCurrentText:)` | underlined run |
| `strikethrough(_:changeCurrentText:)` | strikethrough run |
| `linkTouch(_:url:withFont:color:changeCurrentText:)` | tappable link run |
| `supperscript(_:withFont:color:offset:changeCurrentText:)` | baseline-offset (superscript) run |
| `appendImageToText(_:)` | append an inline `UIImage` attachment |
| `normal(_:)` | append an unstyled run |
`changeCurrentText: true` restyles the first occurrence of the text already in
the string instead of appending.
```swift
let s = NSMutableAttributedString()
.customize("Total: ", withFont: .systemFont(ofSize: 14))
.customize("R$ 10", withFont: .boldSystemFont(ofSize: 14), color: .label)
.supperscript("00", withFont: .systemFont(ofSize: 9), offset: 6)
label.attributedText = s
```
### `func canSetAsLink(textToFind: String, linkURL: String) -> Bool`
Adds a `.link` attribute to the first match; returns whether it was found.
### `func attributtedString() -> NSAttributedString`
Immutable copy of the whole string.
### `func height(withConstrainedWidth:) -> CGFloat` / `func width(withConstrainedHeight:) -> CGFloat`
Bounding size for the attributed content.
</details>
<details>
<summary><b>Bytes</b> — <code>Sequence where Element == UInt8</code></summary>
### `var data: Data`
Wrap the byte sequence in `Data`.
### `var base64Decoded: Data?`
Interpret the bytes as Base64 text and decode.
### `var string: String?`
Decode the bytes as UTF-8.
```swift
let bytes: [UInt8] = [0x68, 0x69]
bytes.data // 2 bytes
bytes.string // "hi"
```
</details>
## Collections & Sequences