A configurable, on-device profanity filter for Apple platforms. Match once against an init-built index of salted word digests; replace matches with a style you choose.
- Swift 6.3+
- iOS 18+ / macOS 15+ / tvOS 18+ / watchOS 11+ / visionOS 2+
Platform floors track Synchronization.Mutex (Swift 6 concurrency). Older OS versions are not supported.
Add the package in Xcode (File → Add Package Dependencies…) or in Package.swift:
dependencies:[.package(url:"https://github.com/AdrianBinDC/ProfanityFilter.git", from:"1.1.0"),],Then depend on the product:
.product(name:"ProfanityFilter",package:"ProfanityFilter"),import ProfanityFilter
// Shared default: emoji replacement + bundled English list
letcleaned=ProfanityFilter.default.censor("What the fuck?")
// "What the 😲😲😲😲?"
// String sugar
letalso="What the fuck?".censored()cleanUp still works but is deprecated — prefer censor / censored().
letstars=ProfanityFilter(
replacement:.repeating("*"),
languageMode:.fixed(.english),)
stars.censor("fuck") // "****"
letfixed=ProfanityFilter(
replacement:.fixed("[censored]"),
languageMode:.fixed(.english),)
fixed.censor("fuck") // "[censored]"
letbullets=ProfanityFilter(
replacement:.custom {String(repeating:"•", count: $0.count)},
languageMode:.fixed(.english),)varlist=WordList(words:["red","hot pink"])
list = list.inserting(["green"]).removing(["red"])letfilter=ProfanityFilter(
replacement:.repeating("*"),
wordList: list,)
filter.censor("a hot pink car") // "a ******** car"A custom wordList ignores language detection — that filter always uses your list.
| Language | Language case | Resource |
|---|---|---|
| English | .english | en.hashes |
| Spanish | .spanish | es.hashes |
| French | .french | fr.hashes |
| Irish | .irish | ga.hashes |
| Arabic | .arabic | ar.hashes |
| Chinese | .chinese | zh.hashes |
// Always English (default)
ProfanityFilter(languageMode:.fixed(.english))
// Spanish list
ProfanityFilter(languageMode:.fixed(.spanish))
// Detect with NaturalLanguage; fall back when short / low confidence
ProfanityFilter(languageMode:.automatic(fallback:.english))
// Union of every bundled list (costlier; useful for mixed text)
ProfanityFilter(languageMode:.allBundled)Caveats
- Very short strings are unreliable for detection (“fuck” alone may not look like English).
.automaticuses the fallback below a minimum length / confidence. - Mixed-language text is hard; prefer
.fixedor.allBundledwhen you know the mix.
Bundled lists are HMAC-SHA256 digests, not plaintext. Corpora are synced from coffee-and-fun/google-profanity-words (MIT) — see THIRD_PARTY_NOTICES.md. Digests are hygiene, not secrecy (a public salt means common dictionaries can still be probed).
make sync-wordlists # bump UPSTREAM_REF in the Makefile first if neededDaily CI opens a PR when upstream releases would change the digests. Details: Maintainer/README.md.
make lint # SwiftLint
make format # SwiftFormat (write)
make format-check # SwiftFormat --lint (CI)
make test# swift test
make sync-wordlists # refresh *.hashes from upstreamCI runs lint / format / test on GitHub Actions, plus a scheduled word-list sync workflow.
Open Package.swift in Xcode, select the ProfanityFilter scheme (and its shared test plan), destination My Mac, then ⌘U.
MIT. See LICENSE. Third-party corpus attribution: THIRD_PARTY_NOTICES.md.