Obfuscate

1.3.0

Aeastr/Obfuscate

What's New

v1.3.0

2026-01-14T16:45:23Z

New

  • Macro expansion tests — verify strings are actually obfuscated, not just decoded correctly
  • GitHub Actions CI/CD — tests run automatically on push/PR to main

Changed

  • Added security disclaimer to README (obfuscation ≠ encryption)
  • Removed Package.resolved from tracking (libraries should test against latest compatible versions)

Fixed

  • Made ObfuscatedString public for macro testing

Obfuscate

Compile-time string obfuscation macro for Swift.

Tests Swift 6.0+ iOS 13+ macOS 10.15+ tvOS 13+ watchOS 6+ visionOS 1+ License: MIT

Overview

A Swift macro that obfuscates string literals at compile-time. Hides strings from static analysis tools like strings, hex editors, and automated scanners.

Good for:

  • Private API usage (class names, selectors)
  • Internal identifiers and feature flags
  • Strings you don't want trivially discoverable

Not for:

  • API keys, tokens, or secrets — these should never be in client code
  • Obfuscation ≠ encryption; a determined attacker with a debugger will always win

Caution

This raises the bar from "trivial" to "annoying" — it's not real security. If a secret is in your binary, assume it can be extracted.

Installation

dependencies: [
    .package(url: "https://github.com/Aeastr/Obfuscate.git", from: "1.1.0")
]
import Obfuscate

Usage

// Default (XOR)
let secret = #Obfuscate("MySecretString")

// With explicit method
let secret = #Obfuscate("MySecretString", .xor)
let secret = #Obfuscate("MySecretString", .bitShift)
let secret = #Obfuscate("MySecretString", .reversed)
let secret = #Obfuscate("MySecretString", .base64)
let secret = #Obfuscate("MySecretString", .bytes)

Note

Xcode will prompt you to trust macros from this package on first use. This is standard for Swift macro packages—click "Trust & Enable" to proceed.

Methods

All methods hide strings from basic static analysis (strings command, hex editors). Ranked by obfuscation strength:

Rank Method Description
1 .xor XOR with random compile-time key (default)
2 .bitShift Bit rotation with random shift amount
3 .reversed Bytes stored reversed, flipped at runtime
4 .base64 String → Base64 → byte array
5 .bytes String → raw UTF-8 byte array

Which to use?

  • .xor — Best. Random key each build, no recognizable patterns, output varies per compilation.
  • .bitShift — Very good. Random rotation each build, bytes are transformed beyond recognition.
  • .reversed — Good. Simple and fast, string isn't readable forwards in the binary.
  • .base64 — Moderate. Recognizable Base64 charset/padding if found, but hides from basic analysis.
  • .bytes — Minimal. Raw UTF-8 bytes are readable with hex editors. Included for completeness.

Tip

For most use cases, .xor or .bitShift are recommended. All methods achieve the same goal—the ranking reflects resistance to manual reverse engineering.

How It Works

At compile-time, the macro transforms your string into executable code that reconstructs it at runtime. The original string never appears in the binary.

.xor — XOR each byte with a random key
#Obfuscate("Hello", .xor)

Becomes:

{
    let bytes: [UInt8] = [171, 158, 169, 169, 168]  // XOR'd bytes
    let key: UInt8 = 203                            // Random key (changes each build)
    return String(bytes: bytes.map { $0 ^ key }, encoding: .utf8)!
}()
.bitShift — Rotate bits by a random amount
#Obfuscate("Hello", .bitShift)

Becomes:

{
    let bytes: [UInt8] = [144, 202, 216, 216, 222]  // Rotated bytes
    let shift: UInt8 = 3                            // Random shift (changes each build)
    return String(bytes: bytes.map { ($0 &>> shift) | ($0 &<< (8 - shift)) }, encoding: .utf8)!
}()
.reversed — Store bytes in reverse order
#Obfuscate("Hello", .reversed)

Becomes:

{
    let bytes: [UInt8] = [111, 108, 108, 101, 72]  // "olleH" reversed
    return String(bytes: bytes.reversed(), encoding: .utf8)!
}()
.base64 — Encode as Base64, store as bytes
#Obfuscate("Hello", .base64)

Becomes:

{
    let characters: [UInt8] = [83, 71, 86, 115, 98, 71, 56, 61]  // "SGVsbG8=" as bytes
    let base64 = String(bytes: characters, encoding: .utf8)!
    let data = Data(base64Encoded: base64.data(using: .utf8)!)!
    return String(data: data, encoding: .utf8)!
}()
.bytes — Store as raw UTF-8 bytes
#Obfuscate("Hello", .bytes)

Becomes:

{
    let bytes: [UInt8] = [72, 101, 108, 108, 111]  // Raw UTF-8
    return String(bytes: bytes, encoding: .utf8)!
}()

License

MIT. See LICENSE for details.

Description

  • Swift Tools 6.0.0
View More Packages from this Author

Dependencies

Last updated: Sun Feb 01 2026 18:19:05 GMT-1000 (Hawaii-Aleutian Standard Time)