RepoPilotOpen in app →

marmelroy/PhoneNumberKit

A Swift framework for parsing, formatting and validating international phone numbers. Inspired by Google's libphonenumber.

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1d ago
  • 14 active contributors
  • MIT licensed
Show 3 more →
  • CI configured
  • Concentrated ownership — top contributor handles 61% of recent commits
  • No test directory detected

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/marmelroy/phonenumberkit)](https://repopilot.app/r/marmelroy/phonenumberkit)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/marmelroy/phonenumberkit on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: marmelroy/PhoneNumberKit

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/marmelroy/PhoneNumberKit shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 1d ago
  • 14 active contributors
  • MIT licensed
  • CI configured
  • ⚠ Concentrated ownership — top contributor handles 61% of recent commits
  • ⚠ No test directory detected

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live marmelroy/PhoneNumberKit repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/marmelroy/PhoneNumberKit.

What it runs against: a local clone of marmelroy/PhoneNumberKit — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in marmelroy/PhoneNumberKit | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>marmelroy/PhoneNumberKit</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of marmelroy/PhoneNumberKit. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/marmelroy/PhoneNumberKit.git
#   cd PhoneNumberKit
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of marmelroy/PhoneNumberKit and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "marmelroy/PhoneNumberKit(\\.git)?\\b" \\
  && ok "origin remote is marmelroy/PhoneNumberKit" \\
  || miss "origin remote is not marmelroy/PhoneNumberKit (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/marmelroy/PhoneNumberKit"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

PhoneNumberKit is a Swift framework that parses, formats, and validates international phone numbers using metadata from Google's libphonenumber library. It handles phone number standardization for 200+ regions, supports real-time AsYouType formatting for text fields, and provides country code conversions—all without native C dependencies. Single-package structure: core parsing logic in PhoneNumberKit/ (ParseManager.swift, PhoneNumberParser.swift, RegexManager.swift), formatting separated into Formatter.swift and PartialFormatter.swift, metadata bundled as resources in PhoneNumberKit/Resources/ with version tracking. Public API exposed through PhoneNumberUtility.swift and PhoneNumber.swift. Tests and examples organized at project root.

👥Who it's for

iOS/macOS/watchOS/tvOS developers building apps requiring international phone number handling (e.g. auth flows, contact management, SMS integration). Developers who need Google libphonenumber accuracy but want pure Swift with lightweight footprint.

🌱Maturity & risk

Production-ready and actively maintained. The repo shows consistent CI/CD pipelines (GitHub Actions for PR tests, automated releases, metadata updates), multiple supported platforms (iOS, macOS, tvOS, watchOS via xcschemes), and comprehensive testing infrastructure. Last activity visible in workflow setup suggests ongoing maintenance.

Low risk for established apps. Single maintainer (marmelroy) is typical for Swift libraries. Metadata must be kept current—repo includes update_metadata.yml workflow suggesting this is managed. No dependency bloat (pure Swift stdlib + metadata). Breaking changes would be flagged in migration guides (OXMIGRATIONGUIDE.md exists). Main risk: relies entirely on libphonenumber metadata accuracy.

Active areas of work

Actively maintaining CI/CD workflows (pr.yml, publish-cocoapods.yml, release.yml). Automated metadata updates via update_metadata.yml workflow suggest continuous synchronization with Google's libphonenumber. Swift version and linting configs updated (swiftformat, swiftlint configurations present).

🚀Get running

git clone https://github.com/marmelroy/PhoneNumberKit.git && cd PhoneNumberKit && open PhoneNumberKit.xcodeproj (for Xcode) OR swift build (via SPM using Package.swift). CocoaPods users: pod install PhoneNumberKit.

Daily commands: xcodebuild -project PhoneNumberKit.xcodeproj -scheme PhoneNumberKit test (run test suite) OR swift test (SPM). For framework use: import PhoneNumberKit; let utility = PhoneNumberUtility(); try utility.parse("+33689017383").

🗺️Map of the codebase

🛠️How to make changes

Parsing logic: PhoneNumberParser.swift and ParseManager.swift. Formatting: Formatter.swift (standard) and PartialFormatter.swift (AsYouType). Validation rules: consult MetadataTypes.swift and MetadataParsing.swift for region metadata structure. Adding new formats: extend PhoneNumberFormatter.swift. Tests are colocated in Xcode project; use .xcodeproj schemes for running subsets.

🪤Traps & gotchas

Metadata must be kept synchronized with Google's libphonenumber—out-of-sync data causes validation failures for new country codes. PhoneNumberUtility is expensive to initialize (parses all metadata into memory); create once per app lifecycle, not per-call. Regex compilation happens lazily; first parse call may show latency spikes. Bundle resources must be included in app target copy phase or parsing will fail silently. No network calls—all validation is offline via bundled data.

💡Concepts to learn

  • Metadata-Driven Validation — PhoneNumberKit loads region-specific validation rules (digit lengths, format patterns) from bundled libphonenumber metadata at runtime, rather than hardcoding—this allows supporting 200+ countries without code changes
  • Lazy Regex Compilation & Caching — RegexManager.swift caches compiled NSRegularExpression objects to avoid recompiling regex patterns on every parse call; critical for performance when parsing 1000+ numbers
  • AsYouType Formatting (Incremental) — PartialFormatter.swift applies region-aware formatting rules incrementally as users type into UITextField, providing real-time visual feedback without blocking interaction
  • Codable Serialization — PhoneNumber+Codable.swift enables JSON encoding/decoding of parsed phone number objects; essential for API communication and local persistence
  • Resource Bundling & Versioning — PhoneNumberKit bundles 100+ MB of libphonenumber metadata XML/JSON in Resources/; .metadata-version file tracks which libphonenumber release is embedded to ensure consistency
  • E.164 International Format — PhoneNumberKit normalizes all numbers to E.164 format (+CC-NNNNNNNNNNN) as canonical representation, enabling consistent comparison and storage across regions
  • google/libphonenumber — The canonical Java/C++ reference implementation that PhoneNumberKit ports to Swift; metadata source of truth
  • googlei18n/libphonenumber — Official i18n-maintained fork of libphonenumber providing up-to-date metadata and validation rules
  • swiftlang/swift-foundation — Swift standard library; PhoneNumberKit relies on Foundation regex and Codable for core functionality
  • CocoaPods/Specs — Package registry where PhoneNumberKit is published; publish-cocoapods.yml automates releases to Specs
  • apple/swift-package-manager — SPM infrastructure used for SwiftPM distribution via Package.swift; governs build reproducibility

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive test coverage for PhoneNumberFormatter edge cases

PhoneNumberKitTests has PartialFormatterTests.swift but lacks dedicated PhoneNumberFormatterTests.swift. The Formatter.swift and PhoneNumberFormatter.swift classes handle critical formatting logic for international phone numbers across different formats (E164, INTERNATIONAL, NATIONAL, RFC3966). A new test suite should cover edge cases like malformed inputs, boundary conditions for different country codes, and format transitions.

  • [ ] Create PhoneNumberKitTests/PhoneNumberFormatterTests.swift
  • [ ] Add tests for E164, INTERNATIONAL, NATIONAL, and RFC3966 formats
  • [ ] Include edge cases: empty strings, null regions, invalid country codes
  • [ ] Test all format combinations from PhoneNumberFormat enum in Constants.swift
  • [ ] Verify output matches libphonenumber behavior for common countries

Add unit tests for MetadataManager and MetadataParsing

MetadataManager.swift and MetadataParsing.swift are critical for loading and parsing PhoneNumberMetadata.json (18+ MB file with country/region data). The existing tests don't have dedicated coverage for metadata initialization, caching, parsing failures, and metadata version updates. This is crucial since update_metadata.sh and update_metadata.yml workflow exist but lack corresponding integration tests.

  • [ ] Create PhoneNumberKitTests/MetadataManagerTests.swift
  • [ ] Test metadata loading from bundle resources (PhoneNumberKit/Resources/PhoneNumberMetadata.json)
  • [ ] Test caching mechanism and singleton pattern
  • [ ] Add tests for metadata parsing failures and corrupt data handling
  • [ ] Verify metadata version tracking matches .metadata-version file

Add iOS UI component integration tests for PhoneNumberTextField and CountryCodePickerViewController

PhoneNumberTextField.swift and CountryCodePickerViewController.swift provide critical UI components but PhoneNumberTextFieldTests.swift only has basic tests. Missing coverage for real-world scenarios like locale changes, keyboard interactions, filtering country lists, and state restoration. CountryCodePickerTableViewCell.swift and CountryCodePickerSectionHeaderView.swift lack dedicated tests.

  • [ ] Expand PhoneNumberKitTests/PhoneNumberTextFieldTests.swift with delegate callback testing
  • [ ] Create PhoneNumberKitTests/CountryCodePickerViewControllerTests.swift
  • [ ] Test country filtering and search functionality in CountryCodePickerViewController
  • [ ] Add tests for PhoneNumberTextFieldDelegate callbacks (validation, formatting during typing)
  • [ ] Test UITableView data source methods for picker cells and section headers

🌿Good first issues

  • Add unit test coverage for edge cases in PartialFormatter.swift (AsYouType formatter) around format transitions (e.g. '+1 (23' → '+1 (234)'), which currently lacks explicit test cases
  • Document the metadata JSON schema and update CLAUDE.md with metadata version management workflow—update_metadata.yml exists but its prerequisites and validation steps aren't documented for contributors
  • Extend PhoneNumberFormatter.swift with a new public method for consistent formatting across all ISO 3166 calling codes, covering missing regional variants (check GitHub issues for 'formatting' keywords)

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 2d547ff — Release 4.2.12 (github-actions[bot])
  • b124a5c — Updated metadata to version metadata/9.0.30 (#902) (github-actions[bot])
  • 840ca3a — Release 4.2.11 (github-actions[bot])
  • 0f299e8 — Updated metadata to version metadata/9.0.29 (#899) (github-actions[bot])
  • c15542f — Release 4.2.10 (github-actions[bot])
  • 73499fd — Updated metadata to version metadata/9.0.28 (#898) (github-actions[bot])
  • f6b6125 — Release 4.2.9 (github-actions[bot])
  • 7d1489f — Updated metadata to version metadata/9.0.27 (#897) (github-actions[bot])
  • aa16e94 — perf: replace DispatchQueue.sync with NSLock in RegexManager to prevent UI freezes (#893) (EugenePetlitskiy)
  • 54f678c — Release 4.2.8 (github-actions[bot])

🔒Security observations

PhoneNumberKit demonstrates generally good security posture as a data parsing library. The main concerns are around metadata handling (XML parsing potential XXE vulnerabilities and automated updates without verification) and the lack of formal security policy. The codebase appears free of SQL injection, XSS, and hardcoded credentials risks due to its nature as a phone number parsing utility. Swift's type safety and the absence of direct infrastructure components mitigate many common vulnerabilities. Recommendations focus on establishing security processes (responsible disclosure policy), hardening metadata management (checksums, pinning), and ensuring dependency monitoring is in place.

  • Medium · Metadata File in Plain XML Format — PhoneNumberKit/Resources/Original/PhoneNumberMetadata.xml. The repository contains PhoneNumberMetadata.xml in the Resources/Original directory. While this is metadata for phone number parsing, storing both XML and JSON versions of the same data increases attack surface and maintenance burden. The XML format could be vulnerable to XXE (XML External Entity) attacks if parsed without proper safeguards. Fix: Ensure XML parsing is configured to disable external entity resolution. Remove the original XML file from distribution if only the JSON version is needed. Verify that NSXMLParser or any XML parsing library used has XXE protections enabled.
  • Medium · Automated Metadata Updates Without Pinning — PhoneNumberKit/Resources/update_metadata.sh, .github/workflows/update_metadata.yml. The update_metadata.sh script and update_metadata.yml workflow suggest automated updates to phone number metadata. Without version pinning or checksum verification, this could introduce unexpected changes or supply chain attacks if the upstream source is compromised. Fix: Implement checksum verification for downloaded metadata files. Pin metadata versions explicitly. Review all automated updates before merging. Consider storing metadata updates in a separate PR for review rather than automatic commits.
  • Low · Missing Security.md or Security Policy — Repository root. No SECURITY.md file found in the repository root. This makes it difficult for security researchers to report vulnerabilities responsibly. Fix: Create a SECURITY.md file following GitHub's security policy template. Include instructions for responsible disclosure and how to contact maintainers with security issues.
  • Low · No Dependency Vulnerability Scanning Configuration — .github configuration. No evidence of dependency scanning tools (e.g., Dependabot configuration) in the provided file structure. The Swift Package Manager and CocoaPods dependencies are not explicitly tracked for known vulnerabilities. Fix: Enable Dependabot for Swift Package Manager dependencies. Add regular security audit workflows. Consider integrating tools like swift-outdated or dependencycheck into CI/CD pipeline.
  • Low · Potential Information Disclosure via Build Artifacts — PhoneNumberKit.xcodeproj/xcshareddata/xcbaselines/. The repository contains test baseline files and Xcode workspace configurations that could expose build-time information. While low risk for this library, these artifacts could reveal system paths or build environment details. Fix: Review .gitignore to ensure build artifacts, temporary files, and developer-specific configurations are properly excluded. Consider adding .xcbaselines/ to gitignore if they are regenerated during builds.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · marmelroy/PhoneNumberKit — RepoPilot