# LCEssentials — Extensions
Foundation, value-type, string, collection, numeric, date, and crypto helpers, plus
the `LCEssentials` namespace itself. UIKit extensions live in
[UIKit.md](UIKit.md); SwiftUI helpers in [SwiftUI.md](SwiftUI.md).
Every section is a collapsible block — click a heading to expand it.
## Contents
- [API & Networking](#api--networking)
- [Strings & Text](#strings--text)
- [Collections & Sequences](#collections--sequences)
- [Numbers & Geometry](#numbers--geometry)
- [Date, Data & Files](#date-data--files)
- [Encoding & Errors](#encoding--errors)
- [Crypto](#crypto)
- [Core — the `LCEssentials` namespace](#core--the-lcessentials-namespace)
---
## API & Networking
`API` is an `actor` wrapping `URLSession` for typed JSON requests and multipart
uploads. This is a summary — the **[full guide is in API.md](API.md)** (all
parameters, error model, client certificates, custom body types, and the
rationale vs. a hand-rolled `URLSession`).
API — typed async requests & uploads
### `API.shared`
The shared `actor` instance. Every call is `await`; nothing runs on the main
thread unless you hop there yourself.
```swift
struct User: Decodable, Sendable { let id: Int; let name: String }
let user: User = try await API.shared.request(
url: "https://api.example.com/users/{id}",
method: .get,
pathParams: ["id": "42"]
)
```
### `request(url:method:body:pathParams:headers:debug:timeoutInterval:networkServiceType:persistConnection:)`
Sends a request and decodes the JSON response into `T: Decodable & Sendable`.
Non-2xx responses throw an `NSError` whose `code` is the HTTP status and whose
`localizedFailureReason` is the response body. When `T == String` the raw body
is returned without JSON decoding.
```swift
struct CreateUser: Encodable, Sendable { let name: String }
// JSON body
let created: User = try await API.shared.request(
url: "https://api.example.com/users",
method: .post,
body: jsonBody(CreateUser(name: "Ana"))
)
// form-url-encoded body
let token: Token = try await API.shared.request(
url: "https://api.example.com/oauth/token",
method: .post,
body: .form(["grant_type": "password", "username": "ana"])
)
```
### `upload(url:method:form:pathParams:headers:debug:timeoutInterval:networkServiceType:)` and its `onProgress:` overload
Uploads a `multipart/form-data` body serialised to a temp file (removed
afterwards) and streamed from disk, so large files never fully load into memory.
```swift
var form = MultipartForm()
form.field("caption", "Sunset")
form.file("photo", data: jpegData, filename: "p.jpg")
form.file("video", url: localVideoURL) // streamed from disk
let result: UploadResult = try await API.shared.upload(
url: "https://api.example.com/media",
form: form,
onProgress: { fraction in
Task { @MainActor in progressView.progress = Float(fraction) }
}
)
```
### Body types — `HTTPBody`
`jsonBody(_:)` wraps any `Encodable & Sendable`; `FormURLEncodedBody` (a.k.a.
`.form([:])`) percent-escapes every value and never drops one; `RawBody` lets you
supply the bytes and `Content-Type` yourself. Conform your own type to `HTTPBody`
and `API` accepts it with no change.
```swift
struct CSVBody: HTTPBody {
let rows: [[String]]
func encoded() throws -> (data: Data, contentType: String) {
let text = rows.map { $0.joined(separator: ",") }.joined(separator: "\n")
return (Data(text.utf8), "text/csv; charset=UTF-8")
}
}
```
### `await API.shared.setupCertification(certData:password:)`
Registers a client certificate (`.p12`) for mutual-TLS on subsequent requests.
```swift
let p12 = try Data(contentsOf: certURL)
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 `