# 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 ## Collections & Sequences ## Numbers & Geometry ## Date, Data & Files ## Encoding & Errors ## Crypto
RIPEMD_160 — RIPEMD-160 digest ### `static func hash(_ message: Data) -> Data` Returns the 20-byte RIPEMD-160 digest of `message`. Pure Swift, no system dependency. Mainly useful for Bitcoin-style address hashing (`RIPEMD160(SHA256(x))`). ```swift let digest = RIPEMD_160.hash(Data("abc".utf8)) digest.count // 20 digest.map { String(format: "%02x", $0) }.joined() // "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc" ```
LCECryptoKitManager — OTP / peppered-login bridge (needs the LCECryptoKit binary) A thin facade over the optional `LCECryptoKit` binary product (enabled by the `LCE_ENABLE_CRYPTO_BINARY` build flag). **When the binary is not linked every method is a no-op** returning `nil` / `""` / `false`, so calling code still compiles and runs. ### `init()` / `init(privateKey:)` Create a manager. The `privateKey` (a.k.a. "hash key") is only needed by the `*WithKey` methods. ```swift let crypto = LCECryptoKitManager() let keyed = LCECryptoKitManager(privateKey: serverHashKey) ``` ### `static func generateKey() -> String` Generates a random AES key string. ```swift let key = LCECryptoKitManager.generateKey() ``` ### `func encodeTP(email:password:) -> String?` / `func decodeOTP(_:) -> String?` Encode an email+password pair into an OTP seed hash, and decode it back. ```swift let hash = crypto.encodeTP(email: "ana@x.com", password: "s3cr3t") let back = crypto.decodeOTP(hash ?? "") ``` ### `func encodeOTPWithKey(email:password:) -> String?` / `func decodeOTPWithKey(_:) -> Bool` Same as above but bound to the instance's `privateKey`; `decodeOTPWithKey` returns whether the hash validates against that key rather than the decoded value. ```swift let keyed = LCECryptoKitManager(privateKey: serverHashKey) let hash = keyed.encodeOTPWithKey(email: "ana@x.com", password: "s3cr3t") let ok = keyed.decodeOTPWithKey(hash ?? "") // Bool ``` ### `static func generateSalt() -> String` Random salt for the salted/iterated/peppered login flow. ```swift let salt = LCECryptoKitManager.generateSalt() ``` ### `static func computeClientHash(email:password:salt:) -> String` Client-side hash of the credentials with the given salt — sent to the server instead of the raw password. ```swift let clientHash = LCECryptoKitManager.computeClientHash( email: "ana@x.com", password: "s3cr3t", salt: salt ) ``` ### `static func computeLoginBearerToken(userId:clientHash:) -> String?` Derives the login bearer token from a user id and the client hash. ```swift let token = LCECryptoKitManager.computeLoginBearerToken(userId: "42", clientHash: clientHash) ``` ### `static func otpEncode(_:) -> String?` / `static func otpDecode(_:) -> String?` One-time-pad encode/decode of an arbitrary string. ```swift let enc = LCECryptoKitManager.otpEncode("secret-value") let dec = LCECryptoKitManager.otpDecode(enc ?? "") // "secret-value" ```
## Core — the `LCEssentials` namespace