From 1d2c595c78b80ea93b34514eccf0d8bd47db99a7 Mon Sep 17 00:00:00 2001 From: Daniel Arantes Loverde Date: Sat, 29 Aug 2026 19:25:15 -0300 Subject: [PATCH] [docs] Extensions.md: Strings & Text section --- Documentation/Extensions.md | 555 +++++++++++++++++++++++++++++++++++- 1 file changed, 554 insertions(+), 1 deletion(-) diff --git a/Documentation/Extensions.md b/Documentation/Extensions.md index ff75537..ad19611 100644 --- a/Documentation/Extensions.md +++ b/Documentation/Extensions.md @@ -119,7 +119,560 @@ await API.shared.setupCertification(certData: p12, password: "cert-pw") ## Strings & Text - +
+String — validation, parsing, formatting, masks, HTML, dates + +### 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 2–20 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 +"hi".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) -> 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 +"

hi

".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, 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 `