Releases: tomisacat/Obfuscated
Obfuscated 2.1.0
Obfuscated 2.1.0
Obfuscated 2.1.0 extends #Obfuscated beyond strings to Int, Bool, Data, and enums. This is an additive minor release — existing string macro usage, built-in methods, and custom step wiring are unchanged.
Requirements
- macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
- Swift 6.2+
- Xcode 16+ (macro plugin support)
Installation
Add Obfuscated via Swift Package Manager:
dependencies: [
.package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.1.0"),
],
targets: [
.target(
name: "<YourAppTarget>",
dependencies: ["Obfuscated"]
),
]For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.
Products
| Product | Use when |
|---|---|
Obfuscated |
Default — built-in methods only; #Obfuscated via ObfuscatedMacros |
ObfuscatedCore |
Direct pipeline access, tests, or custom step libraries |
ObfuscatedMacroSupport |
Building a user-owned macro plugin with custom ObfuscationStep types |
Highlights
Typed value obfuscation
#Obfuscated now accepts compile-time literals for multiple types and returns ordinary values at runtime — no wrapper types, no manual decode:
| Type | Example |
|---|---|
String |
#Obfuscated("secret", methods: [...]) |
Int |
#Obfuscated(443, methods: [...]) |
Bool |
#Obfuscated(true, methods: [...]) |
Data |
#Obfuscated([0xDE, 0xAD], methods: [...]) |
| Enum case | #Obfuscated(Environment.production, methods: [...]) — requires CaseIterable |
RawRepresentable |
#Obfuscated(1, as: Color.self, methods: [...]) |
let port: Int = #Obfuscated(443, methods: [.xor(key: 0x11)])
let enabled: Bool = #Obfuscated(true, methods: [.xor(key: 1)])
let token: Data = #Obfuscated([0xDE, 0xAD, 0xBE, 0xEF], methods: [.xor(key: 0x5A)])
enum Environment: CaseIterable { case production, staging }
let env: Environment = #Obfuscated(Environment.production, methods: [.xor(key: 0x3C)])
enum Role: String { case admin, guest }
let role: Role = #Obfuscated("admin", as: Role.self, methods: [.xor(key: 0x2A)])Enum semantics:
Type.caseobfuscates the case name as UTF-8. RequiresCaseIterableandSendable. Applies to anyCaseIterableenum — even when it also has anIntorStringraw value.as: Type.selfobfuscates the raw value (Int→ 8-byte big-endianInt64;String→ UTF-8). RequiresRawRepresentable. Use when you want the stored raw value hidden.- Both forms are valid when an enum conforms to
CaseIterableandRawRepresentable:#Obfuscated(Color.red, ...)hides"red";#Obfuscated(1, as: Color.self, ...)hides1. - Enums without a raw value (e.g.
Environment) only support theType.caseform.
See README — Limitations for the full caveat list.
All types share the same obfuscation method pipeline (XOR, crypto, custom steps, etc.) after per-type serialization to plaintext bytes.
New public types
| Type | Role |
|---|---|
ObfuscatedValue |
Protocol for serializing values to/from plaintext bytes before/after the pipeline |
ObfuscatedEnumSupport |
CaseIterable case lookup by decoded case name |
ObfuscatedRawRepresentableSupport |
Encode/decode helpers for RawRepresentable raw values |
Built-in ObfuscatedValue conformances: String, Int, Bool, Data.
Re-exported from Obfuscated as public typealias ObfuscatedValue.
Pipeline and runtime extensions
| API | Role |
|---|---|
ObfuscationPipeline.encode(bytes:methods:) |
Byte-level encode entry point |
ObfuscationPipeline.encode<T: ObfuscatedValue>(_:methods:) |
Generic value encode |
ObfuscationPipeline.decodeBytes(_:methods:) |
Reverse pipeline to plaintext bytes |
ObfuscationPipeline.decode<T: ObfuscatedValue>(_:methods:as:) |
Generic value decode |
ObfuscatedRuntime._decode(bytes:methods:material:as:) |
Typed runtime decode for macro expansions |
ObfuscatedRuntime._decodeRawRepresentable(...) |
Raw-representable enum decode |
ObfuscatedRuntime._decodeCaseIterable(...) |
Case-name enum decode |
Existing ObfuscationPipeline.encode(_:methods:) / decode(_:methods:) -> String and ObfuscatedRuntime._decode(bytes:methods:material:) -> String are unchanged.
Demo updates
The demo app adds a Typed Values section covering Int, Bool, Data, CaseIterable enum cases, and RawRepresentable enums. ObfuscatedDemoKit mirrors the new macro overloads.
Migration from 2.0.0
-
Typical app (strings only) — bump the dependency and rebuild:
.package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.1.0")
No source changes expected.
-
Adopting typed values — use the new overloads where compile-time literals fit your use case. Variables and runtime expressions are still not supported.
-
Custom macro plugin authors — rebuild your plugin. If you exhaustively switch on
ObfuscatedMacroError, add cases formissingValueLiteralandinvalidTypeExpression. -
Pin to 2.0.0 if you are not ready to upgrade:
.package(url: "https://github.com/tomisacat/Obfuscated.git", exact: "2.0.0")
Unchanged from 2.0.0
- String
#Obfuscatedoverload, expansion shape, and default plugin (ObfuscatedMacros) - All built-in and custom obfuscation methods
ObfuscationMethod,ObfuscationError, andCryptoMaterialshapes- Static string interpolation (
\("...")literals folded at compile time) - Custom step setup via
ObfuscationStepand user-owned macro plugins - Platform and Swift version requirements
Limitations
See also the canonical list in README — Limitations.
- Compile-time literals only — not variables or runtime expressions
- Enum cases via
Type.caserequireCaseIterableandSendable; associated values are not supported Type.caseworks for anyCaseIterableenum, includingInt- orString-backed enums that also conform toRawRepresentable— it obfuscates the case name, not the raw value- When an enum is both
CaseIterableandRawRepresentable, choose the overload by what you want hidden:#Obfuscated(Color.red, ...)→ name"red";#Obfuscated(1, as: Color.self, ...)→ raw value1 - Enums without
RawRepresentable(e.g.Environment.production) only support theType.caseform
Documentation
- README — quick start, typed values, and method reference
- ARCHITECTURE.md — Mermaid diagrams and data flow
- DOCUMENTATION.md — full source reference
- CUSTOM_OBFUSCATION_STEPS.md — custom step and macro plugin setup
- Demo app — SwiftUI catalog on iOS and macOS
Testing
60 unit tests cover encode/decode round-trips, typed values (ObfuscatedValueTests), built-in and custom step pipelines, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.
License
MIT License — see LICENSE.
Tag: 2.1.0
Obfuscated 2.0.0
Obfuscated 2.0.0
Obfuscated 2.0.0 adds user-defined obfuscation steps, extracts shared macro infrastructure into ObfuscatedMacroSupport, and publishes ObfuscatedCore as a standalone product. Built-in #Obfuscated usage is unchanged for typical apps; this is a major release because public enums and CryptoMaterial gained new cases and fields.
Requirements
- macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
- Swift 6.2+
- Xcode 16+ (macro plugin support)
Installation
Add Obfuscated via Swift Package Manager:
dependencies: [
.package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.0.0"),
],
targets: [
.target(
name: "<YourAppTarget>",
dependencies: ["Obfuscated"]
),
]For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.
Products
| Product | Use when |
|---|---|
Obfuscated |
Default — built-in methods only; #Obfuscated via ObfuscatedMacros |
ObfuscatedCore |
Direct pipeline access, tests, or custom step libraries |
ObfuscatedMacroSupport |
Building a user-owned macro plugin with custom ObfuscationStep types |
Highlights
Custom obfuscation steps
Define your own transforms with the ObfuscationStep protocol and use them in #Obfuscated via .custom(id:parameters:):
let secret = #Obfuscated(
"Custom protected secret",
methods: [.custom(id: "rot13", parameters: ObfuscationParameters(bytes: [13]))]
)Custom steps require a user-owned macro plugin target that links ObfuscatedMacroSupport and registers step types before expansion. Macro expansion runs in a plugin process that cannot import your app module.
Full setup guide: docs/CUSTOM_OBFUSCATION_STEPS.md
Working example in this repo: Demo/ObfuscatedDemoSupport (ObfuscatedDemoKit + ObfuscatedDemoMacros + DemoRot13Step).
ObfuscatedMacroSupport extraction
Macro parsing, expansion building, and ObfuscatedMacro now live in the ObfuscatedMacroSupport library. ObfuscatedMacros is a thin default compiler plugin (built-in methods only). Advanced users link ObfuscatedMacroSupport when building custom plugins.
New public types
| Type | Role |
|---|---|
ObfuscationStep |
Protocol for user-defined encode/decode transforms |
ObfuscationParameters |
Literal byte parameters embedded in macro expansions |
ObfuscationStepRegistry |
Registers custom steps for pipeline + macro encode |
CustomMaterialEntry |
Per-step persisted state in CryptoMaterial |
ObfuscationMacroConfiguration |
Plugin init hook to run step registration |
Re-exported from Obfuscated alongside existing ObfuscationMethod, ObfuscatedKey, etc.
Demo package
The demo app's custom-step wiring moved out of the root package into a local SPM package at Demo/ObfuscatedDemoSupport/. The Xcode demo imports ObfuscatedDemoKit from that package instead of depending on the root Obfuscated product directly.
Breaking changes (from 1.0.0)
ObfuscationMethod — new case
case custom(id: String, parameters: ObfuscationParameters)Impact: Exhaustive switch statements over ObfuscationMethod must handle .custom or add @unknown default.
No impact for typical usage: #Obfuscated("...", methods: [.xor(key: 0x5A), .base64]) with built-in method literals.
ObfuscationError — new cases
unknownCustomStep(String)missingCustomMaterial
Impact: Exhaustive switch statements over ObfuscationError must handle the new cases.
No impact for typical #Obfuscated callers — ObfuscatedRuntime._decode catches errors internally.
CryptoMaterial — new field
public var customEntries: [CustomMaterialEntry]
public init(entries: [CryptoEntry] = [], customEntries: [CustomMaterialEntry] = [])Impact: Code that pattern-matches CryptoMaterial exhaustively or assumes a fixed shape may need updates.
No impact for CryptoMaterial(entries: [...]) call sites — customEntries defaults to [].
Macro module layout
Macro implementation code moved from ObfuscatedMacros to ObfuscatedMacroSupport. The default #Obfuscated declaration still points at ObfuscatedMacros.ObfuscatedMacro.
Impact: Projects that imported or @testable imported ObfuscatedMacros internals directly must migrate to ObfuscatedMacroSupport.
No impact for import Obfuscated consumers.
Migration from 1.0.0
-
Typical app (built-in methods only) — bump the dependency and rebuild:
.package(url: "https://github.com/tomisacat/Obfuscated.git", from: "2.0.0")
No source changes expected.
-
Exhaustive enum switches — add cases for
.custom,unknownCustomStep, andmissingCustomMaterial, or use@unknown default. -
Custom steps (new in 2.0) — follow CUSTOM_OBFUSCATION_STEPS.md:
- Implement
ObfuscationStep - Add a
.macrotarget withObfuscatedMacroSupport - Register steps in the plugin
initand at app launch for runtime decode - Point
#Obfuscatedat your plugin with#externalMacro
- Implement
-
Pin to 1.0.0 if you are not ready to upgrade:
.package(url: "https://github.com/tomisacat/Obfuscated.git", exact: "1.0.0")
Unchanged from 1.0.0
#Obfuscatedmacro signature and default plugin module (ObfuscatedMacros)- All built-in obfuscation methods (XOR, bit shift, bit OR, Base64, AES-GCM, ChaCha, HMAC, HKDF, ECIES)
- Static string interpolation (
\("...")literals folded at compile time) ObfuscatedRuntime._decode(bytes:methods:material:)entry point- Platform and Swift version requirements
Documentation
- README — quick start, products, and method reference
- ARCHITECTURE.md — Mermaid diagrams and data flow
- DOCUMENTATION.md — full source reference
- CUSTOM_OBFUSCATION_STEPS.md — custom step and macro plugin setup
- Demo app — SwiftUI catalog on iOS and macOS
Testing
48 unit tests cover encode/decode round-trips, built-in and custom step pipelines, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.
License
MIT License — see LICENSE.
Tag: 2.0.0
Obfuscated 1.0.0
Obfuscated 1.0.0
Obfuscated is a compile-time Swift macro that obfuscates string literals at build time and returns ordinary String values at runtime.
Requirements
- macOS 15+, iOS 14+, tvOS 14+, watchOS 7+, macCatalyst 14+
- Swift 6.2+
- Xcode 16+ (macro plugin support)
Installation
Add Obfuscated via Swift Package Manager:
dependencies: [
.package(url: "https://github.com/tomisacat/Obfuscated.git", from: "1.0.0"),
],
targets: [
.target(
name: "<YourAppTarget>",
dependencies: ["Obfuscated"]
),
]For an Xcode app target, use File → Add Package Dependencies and enter https://github.com/tomisacat/Obfuscated.git.
Highlights
#Obfuscated macro
Use #Obfuscated("secret", methods: [...]) to embed obfuscated bytes and crypto material in your binary. The macro expands to a hidden decode call; callers receive a normal String with no wrapper type or manual decryption.
Obfuscation pipeline
Chain lightweight transforms (XOR, bit shift, bit OR, Base64) with CryptoKit-backed methods (AES-GCM, ChaCha20-Poly1305, HMAC keystream, HKDF + AEAD, Curve25519/P-256 ECIES). Pass nil for crypto key material to generate random values at compile time, or supply explicit ObfuscatedKey, ObfuscatedNonce, ObfuscatedSalt, and ObfuscatedInfo for reproducible output.
Static string interpolation
String literals with static \("...") segments are folded at compile time and obfuscated as one value. Runtime variables inside \(...) are not supported.
Documentation
- README — quick start and method reference
- ARCHITECTURE.md — Mermaid diagrams and data flow
- DOCUMENTATION.md — full source reference
- Demo app — SwiftUI catalog of every method on iOS and macOS
Testing
Unit tests cover encode/decode round-trips, crypto edge cases, macro parsing, and expansion. CI runs swift test and demo app builds on every push and pull request to main.
License
MIT License — see LICENSE.
Tag: v1.0.0