{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2025-12-05-introducing-swift-huggingface-the-complete-swift-client-for/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "Today, we're announcing [swift-huggingface](https://github.com/huggingface/swift-huggingface), a new Swift package that provides a complete client for the Hugging Face Hub.\n\nYou can start using it today as a standalone package, and it will soon integrate into swift-transformers as a replacement for its current `HubApi` implementation.\n\n## The Problem\n\nWhen we released [swift-transformers 1.0](https://huggingface.co/blog/swift-transformers) earlier this year, we heard loud and clear from the community:\n\n- **Downloads were slow and unreliable.** Large model files (often several gigabytes) would fail partway through with no way to resume. Developers resorted to manually downloading models and bundling them with their apps — defeating the purpose of dynamic model loading.\n- **No shared cache with the Python ecosystem.** The Python `transformers` library stores models in `~/.cache/huggingface/hub`. Swift apps downloaded to a different location with a different structure. If you'd already downloaded a model using the Python CLI, you'd download it again for your Swift app.\n- **Authentication is confusing.** Where should tokens come from? Environment variables? Files? Keychain? The answer is, *\"It depends\"*, and the existing implementation didn't make the options clear.\n\n## Introducing swift-huggingface\n\nswift-huggingface is a ground-up rewrite focused on reliability and developer experience. It provides:\n\n- **Complete Hub API coverage** — models, datasets, spaces, collections, discussions, and more\n- **Robust file operations** — progress tracking, resume support, and proper error handling\n- **Python-compatible cache** — share downloaded models between Swift and Python clients\n- **Flexible authentication** — a `TokenProvider` pattern that makes credential sources explicit\n- **OAuth support** — first-class support for user-facing apps that need to authenticate users\n- **Xet storage backend support** *(Coming soon!)* — chunk-based deduplication for significantly faster downloads\n\nLet's look at some examples.\n\n* * *\n\n## Flexible Authentication with TokenProvider\n\nOne of the biggest improvements is how authentication works. The `TokenProvider` pattern makes it explicit where credentials come from:\n\n```swift\nimport HuggingFace\n\n// For development: auto-detect from environment and standard locations\n// Checks HF_TOKEN, HUGGING_FACE_HUB_TOKEN, ~/.cache/huggingface/token, etc.\nlet client = HubClient.default\n\n// For CI/CD: explicit token\nlet client = HubClient(tokenProvider: .static(\"hf_xxx\"))\n\n// For production apps: read from Keychain\nlet client = HubClient(tokenProvider: .keychain(service: \"com.myapp\", account: \"hf_token\"))\n```\n\nThe auto-detection follows the same conventions as the Python `huggingface_hub` library:\n\n1. `HF_TOKEN` environment variable\n2. `HUGGING_FACE_HUB_TOKEN` environment variable\n3. `HF_TOKEN_PATH` environment variable (path to token file)\n4. `$HF_HOME/token` file\n5. `~/.cache/huggingface/token` (standard HF CLI location)\n6. `~/.huggingface/token` (fallback location)\n\nThis means if you've already logged in with `hf auth login`, swift-huggingface will automatically find and use that token.\n\n## OAuth for User-Facing Apps\n\nBuilding an app where users sign in with their Hugging Face account? swift-huggingface includes a complete OAuth 2.0 implementation:\n\n```swift\nimport HuggingFace\n\n// Create authentication manager\nlet authManager = try HuggingFaceAuthenticationManager(\n    clientID: \"your_client_id\",\n    redirectURL: URL(string: \"yourapp://oauth/callback\")!,\n    scope: [.openid, .profile, .email],\n    keychainService: \"com.yourapp.huggingface\",\n    keychainAccount: \"user_token\"\n)\n\n// Sign in user (presents system browser)\ntry await authManager.signIn()\n\n// Use with Hub client\nlet client = HubClient(tokenProvider: .oauth(manager: authManager))\n\n// Tokens are automatically refreshed when needed\nlet userInfo = try await client.whoami()\nprint(\"Signed in as: \\(userInfo.name)\")\n```\n\nThe OAuth manager handles token storage in Keychain, automatic refresh, and secure sign-out. No more manual token management.\n\n## Reliable Downloads\n\nDownloading large models is now straightforward with proper progress tracking and resume support:\n\n```swift\n// Download with progress tracking\nlet progress = Progress(totalUnitCount: 0)\n\nTask {\n    for await _ in progress.publisher(for: \\.fractionCompleted).values {\n        print(\"Download: \\(Int(progress.fractionCompleted * 100))%\")\n    }\n}\n\nlet fileURL = try await client.downloadFile(\n    at: \"model.safetensors\",\n    from: \"microsoft/phi-2\",\n    to: destinationURL,\n    progress: progress\n)\n```\n\nIf a download is interrupted, you can resume it:\n\n```swift\n// Resume from where you left off\nlet fileURL = try await client.resumeDownloadFile(\n    resumeData: savedResumeData,\n    to: destinationURL,\n    progress: progress\n)\n```\n\nFor downloading entire model repositories, `downloadSnapshot` handles everything:\n\n```swift\nlet modelDir = try await client.downloadSnapshot(\n    of: \"mlx-community/Llama-3.2-1B-Instruct-4bit\",\n    to: cacheDirectory,\n    matching: [\"*.safetensors\", \"*.json\"],  // Only download what you need\n    progressHandler: { progress in\n        print(\"Downloaded \\(progress.completedUnitCount) of \\(progress.totalUnitCount) files\")\n    }\n)\n```\n\nThe snapshot function tracks metadata for each file, so subsequent calls only download files that have changed.\n\n## Shared Cache with Python\n\nRemember the second problem we mentioned? *\"No shared cache with the Python ecosystem.\"* That's now solved.\n\nswift-huggingface implements a Python-compatible cache structure that allows seamless sharing between Swift and Python clients:\n\n```\n~/.cache/huggingface/hub/\n├── models--deepseek-ai--DeepSeek-V3.2/\n│   ├── blobs/\n│   │   └── <etag>           # actual file content\n│   ├── refs/\n│   │   └── main             # contains commit hash\n│   └── snapshots/\n│       └── <commit_hash>/\n│           └── config.json  # symlink → ../../blobs/<etag>\n```\n\nThis means:\n\n- **Download once, use everywhere.** If you've already downloaded a model with the `hf` CLI or the Python library, swift-huggingface will find it automatically.\n- **Content-addressed storage.** Files are stored by their ETag in the `blobs/` directory. If two revisions share the same file, it's only stored once.\n- **Symlinks for efficiency.** Snapshot directories contain symlinks to blobs, minimizing disk usage while maintaining a clean file structure.\n\nThe cache location follows the same environment variable conventions as Python:\n\n1. `HF_HUB_CACHE` environment variable\n2. `HF_HOME` environment variable + `/hub`\n3. `~/.cache/huggingface/hub` (default)\n\nYou can also use the cache directly:\n\n```swift\nlet cache = HubCache.default\n\n// Check if a file is already cached\nif let cachedPath = cache.cachedFilePath(\n    repo: \"deepseek-ai/DeepSeek-V3.2\",\n    kind: .model,\n    revision: \"main\",\n    filename: \"config.json\"\n) {\n    let data = try Data(contentsOf: cachedPath)\n    // Use cached file without any network request\n}\n```\n\nTo prevent race conditions when multiple processes access the same cache, swift-huggingface uses file locking ([`flock(2)`](https://man7.org/linux/man-pages/man2/flock.2.html)).\n\n## Before and After\n\nHere's what downloading a model snapshot looked like with the old `HubApi`:\n\n```swift\n// Before: HubApi in swift-transformers\nlet hub = HubApi()\nlet repo = Hub.Repo(id: \"mlx-community/Llama-3.2-1B-Instruct-4bit\")\n\n// No progress tracking, no resume, errors swallowed\nlet modelDir = try await hub.snapshot(\n    from: repo,\n    matching: [\"*.safetensors\", \"*.json\"]\n) { progress in\n    // Progress object exists but wasn't always accurate\n    print(progress.fractionCompleted)\n}\n```\n\nAnd here's the same operation with swift-huggingface:\n\n```swift\n// After: swift-huggingface\nlet client = HubClient.default\n\nlet modelDir = try await client.downloadSnapshot(\n    of: \"mlx-community/Llama-3.2-1B-Instruct-4bit\",\n    to: cacheDirectory,\n    matching: [\"*.safetensors\", \"*.json\"],\n    progressHandler: { progress in\n        // Accurate progress per file\n        print(\"\\(progress.completedUnitCount)/\\(progress.totalUnitCount) files\")\n    }\n)\n```\n\nThe API is similar, but the implementation is completely different — built on `URLSession` download tasks with proper delegate handling, resume data support, and metadata tracking.\n\n## Beyond Downloads\n\nBut wait, there's more! swift-huggingface contains a complete Hub client:\n\n```swift\n// List trending models\nlet models = try await client.listModels(\n    filter: \"library:mlx\",\n    sort: \"trending\",\n    limit: 10\n)\n\n// Get model details\nlet model = try await client.getModel(\"mlx-community/Llama-3.2-1B-Instruct-4bit\")\nprint(\"Downloads: \\(model.downloads ?? 0)\")\nprint(\"Likes: \\(model.likes ?? 0)\")\n\n// Work with collections\nlet collections = try await client.listCollections(owner: \"huggingface\", sort: \"trending\")\n\n// Manage discussions\nlet discussions = try await client.listDiscussions(kind: .model, \"username/my-model\")\n```\n\nAnd that's not all! swift-huggingface has everything you need to interact with [Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers/index), giving your app instant access to hundreds of machine learning models, powered by world-class inference providers:\n\n```swift\nimport HuggingFace\n\n// Create a client (uses auto-detected credentials from environment)\nlet client = InferenceClient.default\n\n// Generate images from a text prompt\nlet response = try await client.textToImage(\n    model: \"black-forest-labs/FLUX.1-schnell\",\n    prompt: \"A serene Japanese garden with cherry blossoms\",\n    provider: .hfInference,\n    width: 1024,\n    height: 1024,\n    numImages: 1,\n    guidanceScale: 7.5,\n    numInferenceSteps: 50,\n    seed: 42\n)\n\n// Save the generated image\ntry response.image.write(to: URL(fileURLWithPath: \"generated.png\"))\n```\n\nCheck the [README](https://github.com/huggingface/swift-huggingface) for a full list of everything that's supported.\n\n## What's Next\n\nWe're actively working on two fronts:\n\n**Integration with swift-transformers.** We have a [pull request in progress](https://github.com/huggingface/swift-transformers/pull/297) to replace `HubApi` with swift-huggingface. This will bring reliable downloads to everyone using swift-transformers, [mlx-swift-lm](https://github.com/ml-explore/mlx-swift-lm), and the broader ecosystem. If you maintain a Swift-based library or app and want help adopting swift-huggingface, reach out — we're happy to help.\n\n**Faster downloads with Xet.** We're adding support for the [Xet storage backend](https://huggingface.co/docs/hub/storage-backends), which enables chunk-based deduplication and significantly faster downloads for large models. More on this soon.\n\n## Try It Out\n\nAdd swift-huggingface to your project:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/huggingface/swift-huggingface.git\", from: \"0.4.0\")\n]\n```\n\nWe'd love your feedback. If you've been frustrated with model downloads in Swift, give this a try and [let us know how it goes](https://github.com/huggingface/swift-huggingface/issues). Your experience reports will help us prioritize what to improve next.\n\n## Resources\n\n- [swift-huggingface on GitHub](https://github.com/huggingface/swift-huggingface)\n- [swift-transformers](https://github.com/huggingface/swift-transformers)\n- [mlx-swift-examples](https://github.com/ml-explore/mlx-swift-examples)\n- [AnyLanguageModel](https://github.com/mattt/AnyLanguageModel)\n\n* * *\n\n*Thanks to the swift-transformers community for the feedback that shaped this project, and to everyone who filed issues and shared their experiences. This is for you.* ❤️\n",
  "authors": [],
  "captured_at": "2026-10-04T12:50:30.804479567+00:00",
  "category": "ai",
  "content_html": "<p>Today, we're announcing <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-huggingface\">swift-huggingface</a>, a new Swift package that provides a complete client for the Hugging Face Hub.</p>\n<p>You can start using it today as a standalone package, and it will soon integrate into swift-transformers as a replacement for its current <code>HubApi</code> implementation.</p>\n<h2>The Problem</h2>\n<p>When we released <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/blog/swift-transformers\">swift-transformers 1.0</a> earlier this year, we heard loud and clear from the community:</p>\n<ul>\n<li><strong>Downloads were slow and unreliable.</strong> Large model files (often several gigabytes) would fail partway through with no way to resume. Developers resorted to manually downloading models and bundling them with their apps — defeating the purpose of dynamic model loading.</li>\n<li><strong>No shared cache with the Python ecosystem.</strong> The Python <code>transformers</code> library stores models in <code>~/.cache/huggingface/hub</code>. Swift apps downloaded to a different location with a different structure. If you'd already downloaded a model using the Python CLI, you'd download it again for your Swift app.</li>\n<li><strong>Authentication is confusing.</strong> Where should tokens come from? Environment variables? Files? Keychain? The answer is, <em>&quot;It depends&quot;</em>, and the existing implementation didn't make the options clear.</li>\n</ul>\n<h2>Introducing swift-huggingface</h2>\n<p>swift-huggingface is a ground-up rewrite focused on reliability and developer experience. It provides:</p>\n<ul>\n<li><strong>Complete Hub API coverage</strong> — models, datasets, spaces, collections, discussions, and more</li>\n<li><strong>Robust file operations</strong> — progress tracking, resume support, and proper error handling</li>\n<li><strong>Python-compatible cache</strong> — share downloaded models between Swift and Python clients</li>\n<li><strong>Flexible authentication</strong> — a <code>TokenProvider</code> pattern that makes credential sources explicit</li>\n<li><strong>OAuth support</strong> — first-class support for user-facing apps that need to authenticate users</li>\n<li><strong>Xet storage backend support</strong> <em>(Coming soon!)</em> — chunk-based deduplication for significantly faster downloads</li>\n</ul>\n<p>Let's look at some examples.</p>\n<hr />\n<h2>Flexible Authentication with TokenProvider</h2>\n<p>One of the biggest improvements is how authentication works. The <code>TokenProvider</code> pattern makes it explicit where credentials come from:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">import HuggingFace\n\n// For development: auto-detect from environment and standard locations\n// Checks HF_TOKEN, HUGGING_FACE_HUB_TOKEN, ~/.cache/huggingface/token, etc.\nlet client = HubClient.default\n\n// For CI/CD: explicit token\nlet client = HubClient(tokenProvider: .static(&quot;hf_xxx&quot;))\n\n// For production apps: read from Keychain\nlet client = HubClient(tokenProvider: .keychain(service: &quot;com.myapp&quot;, account: &quot;hf_token&quot;))\n</span></code></pre>\n<p>The auto-detection follows the same conventions as the Python <code>huggingface_hub</code> library:</p>\n<ol>\n<li><code>HF_TOKEN</code> environment variable</li>\n<li><code>HUGGING_FACE_HUB_TOKEN</code> environment variable</li>\n<li><code>HF_TOKEN_PATH</code> environment variable (path to token file)</li>\n<li><code>$HF_HOME/token</code> file</li>\n<li><code>~/.cache/huggingface/token</code> (standard HF CLI location)</li>\n<li><code>~/.huggingface/token</code> (fallback location)</li>\n</ol>\n<p>This means if you've already logged in with <code>hf auth login</code>, swift-huggingface will automatically find and use that token.</p>\n<h2>OAuth for User-Facing Apps</h2>\n<p>Building an app where users sign in with their Hugging Face account? swift-huggingface includes a complete OAuth 2.0 implementation:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">import HuggingFace\n\n// Create authentication manager\nlet authManager = try HuggingFaceAuthenticationManager(\n    clientID: &quot;your_client_id&quot;,\n    redirectURL: URL(string: &quot;yourapp://oauth/callback&quot;)!,\n    scope: [.openid, .profile, .email],\n    keychainService: &quot;com.yourapp.huggingface&quot;,\n    keychainAccount: &quot;user_token&quot;\n)\n\n// Sign in user (presents system browser)\ntry await authManager.signIn()\n\n// Use with Hub client\nlet client = HubClient(tokenProvider: .oauth(manager: authManager))\n\n// Tokens are automatically refreshed when needed\nlet userInfo = try await client.whoami()\nprint(&quot;Signed in as: \\(userInfo.name)&quot;)\n</span></code></pre>\n<p>The OAuth manager handles token storage in Keychain, automatic refresh, and secure sign-out. No more manual token management.</p>\n<h2>Reliable Downloads</h2>\n<p>Downloading large models is now straightforward with proper progress tracking and resume support:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">// Download with progress tracking\nlet progress = Progress(totalUnitCount: 0)\n\nTask {\n    for await _ in progress.publisher(for: \\.fractionCompleted).values {\n        print(&quot;Download: \\(Int(progress.fractionCompleted * 100))%&quot;)\n    }\n}\n\nlet fileURL = try await client.downloadFile(\n    at: &quot;model.safetensors&quot;,\n    from: &quot;microsoft/phi-2&quot;,\n    to: destinationURL,\n    progress: progress\n)\n</span></code></pre>\n<p>If a download is interrupted, you can resume it:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">// Resume from where you left off\nlet fileURL = try await client.resumeDownloadFile(\n    resumeData: savedResumeData,\n    to: destinationURL,\n    progress: progress\n)\n</span></code></pre>\n<p>For downloading entire model repositories, <code>downloadSnapshot</code> handles everything:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">let modelDir = try await client.downloadSnapshot(\n    of: &quot;mlx-community/Llama-3.2-1B-Instruct-4bit&quot;,\n    to: cacheDirectory,\n    matching: [&quot;*.safetensors&quot;, &quot;*.json&quot;],  // Only download what you need\n    progressHandler: { progress in\n        print(&quot;Downloaded \\(progress.completedUnitCount) of \\(progress.totalUnitCount) files&quot;)\n    }\n)\n</span></code></pre>\n<p>The snapshot function tracks metadata for each file, so subsequent calls only download files that have changed.</p>\n<h2>Shared Cache with Python</h2>\n<p>Remember the second problem we mentioned? <em>&quot;No shared cache with the Python ecosystem.&quot;</em> That's now solved.</p>\n<p>swift-huggingface implements a Python-compatible cache structure that allows seamless sharing between Swift and Python clients:</p>\n<pre><code><span class=\"code-snippet\">~/.cache/huggingface/hub/\n├── models--deepseek-ai--DeepSeek-V3.2/\n│   ├── blobs/\n│   │   └── &lt;etag&gt;           # actual file content\n│   ├── refs/\n│   │   └── main             # contains commit hash\n│   └── snapshots/\n│       └── &lt;commit_hash&gt;/\n│           └── config.json  # symlink → ../../blobs/&lt;etag&gt;\n</span></code></pre>\n<p>This means:</p>\n<ul>\n<li><strong>Download once, use everywhere.</strong> If you've already downloaded a model with the <code>hf</code> CLI or the Python library, swift-huggingface will find it automatically.</li>\n<li><strong>Content-addressed storage.</strong> Files are stored by their ETag in the <code>blobs/</code> directory. If two revisions share the same file, it's only stored once.</li>\n<li><strong>Symlinks for efficiency.</strong> Snapshot directories contain symlinks to blobs, minimizing disk usage while maintaining a clean file structure.</li>\n</ul>\n<p>The cache location follows the same environment variable conventions as Python:</p>\n<ol>\n<li><code>HF_HUB_CACHE</code> environment variable</li>\n<li><code>HF_HOME</code> environment variable + <code>/hub</code></li>\n<li><code>~/.cache/huggingface/hub</code> (default)</li>\n</ol>\n<p>You can also use the cache directly:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">let cache = HubCache.default\n\n// Check if a file is already cached\nif let cachedPath = cache.cachedFilePath(\n    repo: &quot;deepseek-ai/DeepSeek-V3.2&quot;,\n    kind: .model,\n    revision: &quot;main&quot;,\n    filename: &quot;config.json&quot;\n) {\n    let data = try Data(contentsOf: cachedPath)\n    // Use cached file without any network request\n}\n</span></code></pre>\n<p>To prevent race conditions when multiple processes access the same cache, swift-huggingface uses file locking (<a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://man7.org/linux/man-pages/man2/flock.2.html\"><code>flock(2)</code></a>).</p>\n<h2>Before and After</h2>\n<p>Here's what downloading a model snapshot looked like with the old <code>HubApi</code>:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">// Before: HubApi in swift-transformers\nlet hub = HubApi()\nlet repo = Hub.Repo(id: &quot;mlx-community/Llama-3.2-1B-Instruct-4bit&quot;)\n\n// No progress tracking, no resume, errors swallowed\nlet modelDir = try await hub.snapshot(\n    from: repo,\n    matching: [&quot;*.safetensors&quot;, &quot;*.json&quot;]\n) { progress in\n    // Progress object exists but wasn&#39;t always accurate\n    print(progress.fractionCompleted)\n}\n</span></code></pre>\n<p>And here's the same operation with swift-huggingface:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">// After: swift-huggingface\nlet client = HubClient.default\n\nlet modelDir = try await client.downloadSnapshot(\n    of: &quot;mlx-community/Llama-3.2-1B-Instruct-4bit&quot;,\n    to: cacheDirectory,\n    matching: [&quot;*.safetensors&quot;, &quot;*.json&quot;],\n    progressHandler: { progress in\n        // Accurate progress per file\n        print(&quot;\\(progress.completedUnitCount)/\\(progress.totalUnitCount) files&quot;)\n    }\n)\n</span></code></pre>\n<p>The API is similar, but the implementation is completely different — built on <code>URLSession</code> download tasks with proper delegate handling, resume data support, and metadata tracking.</p>\n<h2>Beyond Downloads</h2>\n<p>But wait, there's more! swift-huggingface contains a complete Hub client:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">// List trending models\nlet models = try await client.listModels(\n    filter: &quot;library:mlx&quot;,\n    sort: &quot;trending&quot;,\n    limit: 10\n)\n\n// Get model details\nlet model = try await client.getModel(&quot;mlx-community/Llama-3.2-1B-Instruct-4bit&quot;)\nprint(&quot;Downloads: \\(model.downloads ?? 0)&quot;)\nprint(&quot;Likes: \\(model.likes ?? 0)&quot;)\n\n// Work with collections\nlet collections = try await client.listCollections(owner: &quot;huggingface&quot;, sort: &quot;trending&quot;)\n\n// Manage discussions\nlet discussions = try await client.listDiscussions(kind: .model, &quot;username/my-model&quot;)\n</span></code></pre>\n<p>And that's not all! swift-huggingface has everything you need to interact with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/inference-providers/index\">Hugging Face Inference Providers</a>, giving your app instant access to hundreds of machine learning models, powered by world-class inference providers:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">import HuggingFace\n\n// Create a client (uses auto-detected credentials from environment)\nlet client = InferenceClient.default\n\n// Generate images from a text prompt\nlet response = try await client.textToImage(\n    model: &quot;black-forest-labs/FLUX.1-schnell&quot;,\n    prompt: &quot;A serene Japanese garden with cherry blossoms&quot;,\n    provider: .hfInference,\n    width: 1024,\n    height: 1024,\n    numImages: 1,\n    guidanceScale: 7.5,\n    numInferenceSteps: 50,\n    seed: 42\n)\n\n// Save the generated image\ntry response.image.write(to: URL(fileURLWithPath: &quot;generated.png&quot;))\n</span></code></pre>\n<p>Check the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-huggingface\">README</a> for a full list of everything that's supported.</p>\n<h2>What's Next</h2>\n<p>We're actively working on two fronts:</p>\n<p><strong>Integration with swift-transformers.</strong> We have a <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-transformers/pull/297\">pull request in progress</a> to replace <code>HubApi</code> with swift-huggingface. This will bring reliable downloads to everyone using swift-transformers, <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/ml-explore/mlx-swift-lm\">mlx-swift-lm</a>, and the broader ecosystem. If you maintain a Swift-based library or app and want help adopting swift-huggingface, reach out — we're happy to help.</p>\n<p><strong>Faster downloads with Xet.</strong> We're adding support for the <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/docs/hub/storage-backends\">Xet storage backend</a>, which enables chunk-based deduplication and significantly faster downloads for large models. More on this soon.</p>\n<h2>Try It Out</h2>\n<p>Add swift-huggingface to your project:</p>\n<pre><code class=\"language-swift\"><span class=\"code-snippet\" data-language=\"Swift\">dependencies: [\n    .package(url: &quot;https://github.com/huggingface/swift-huggingface.git&quot;, from: &quot;0.4.0&quot;)\n]\n</span></code></pre>\n<p>We'd love your feedback. If you've been frustrated with model downloads in Swift, give this a try and <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-huggingface/issues\">let us know how it goes</a>. Your experience reports will help us prioritize what to improve next.</p>\n<h2>Resources</h2>\n<ul>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-huggingface\">swift-huggingface on GitHub</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/huggingface/swift-transformers\">swift-transformers</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/ml-explore/mlx-swift-examples\">mlx-swift-examples</a></li>\n<li><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://github.com/mattt/AnyLanguageModel\">AnyLanguageModel</a></li>\n</ul>\n<hr />\n<p><em>Thanks to the swift-transformers community for the feedback that shaped this project, and to everyone who filed issues and shared their experiences. This is for you.</em> ❤️</p>\n",
  "content_markdown": "Today, we're announcing [swift-huggingface](https://github.com/huggingface/swift-huggingface), a new Swift package that provides a complete client for the Hugging Face Hub.\n\nYou can start using it today as a standalone package, and it will soon integrate into swift-transformers as a replacement for its current `HubApi` implementation.\n\n## The Problem\n\nWhen we released [swift-transformers 1.0](https://huggingface.co/blog/swift-transformers) earlier this year, we heard loud and clear from the community:\n\n- **Downloads were slow and unreliable.** Large model files (often several gigabytes) would fail partway through with no way to resume. Developers resorted to manually downloading models and bundling them with their apps — defeating the purpose of dynamic model loading.\n- **No shared cache with the Python ecosystem.** The Python `transformers` library stores models in `~/.cache/huggingface/hub`. Swift apps downloaded to a different location with a different structure. If you'd already downloaded a model using the Python CLI, you'd download it again for your Swift app.\n- **Authentication is confusing.** Where should tokens come from? Environment variables? Files? Keychain? The answer is, *\"It depends\"*, and the existing implementation didn't make the options clear.\n\n## Introducing swift-huggingface\n\nswift-huggingface is a ground-up rewrite focused on reliability and developer experience. It provides:\n\n- **Complete Hub API coverage** — models, datasets, spaces, collections, discussions, and more\n- **Robust file operations** — progress tracking, resume support, and proper error handling\n- **Python-compatible cache** — share downloaded models between Swift and Python clients\n- **Flexible authentication** — a `TokenProvider` pattern that makes credential sources explicit\n- **OAuth support** — first-class support for user-facing apps that need to authenticate users\n- **Xet storage backend support** *(Coming soon!)* — chunk-based deduplication for significantly faster downloads\n\nLet's look at some examples.\n\n* * *\n\n## Flexible Authentication with TokenProvider\n\nOne of the biggest improvements is how authentication works. The `TokenProvider` pattern makes it explicit where credentials come from:\n\n```swift\nimport HuggingFace\n\n// For development: auto-detect from environment and standard locations\n// Checks HF_TOKEN, HUGGING_FACE_HUB_TOKEN, ~/.cache/huggingface/token, etc.\nlet client = HubClient.default\n\n// For CI/CD: explicit token\nlet client = HubClient(tokenProvider: .static(\"hf_xxx\"))\n\n// For production apps: read from Keychain\nlet client = HubClient(tokenProvider: .keychain(service: \"com.myapp\", account: \"hf_token\"))\n```\n\nThe auto-detection follows the same conventions as the Python `huggingface_hub` library:\n\n1. `HF_TOKEN` environment variable\n2. `HUGGING_FACE_HUB_TOKEN` environment variable\n3. `HF_TOKEN_PATH` environment variable (path to token file)\n4. `$HF_HOME/token` file\n5. `~/.cache/huggingface/token` (standard HF CLI location)\n6. `~/.huggingface/token` (fallback location)\n\nThis means if you've already logged in with `hf auth login`, swift-huggingface will automatically find and use that token.\n\n## OAuth for User-Facing Apps\n\nBuilding an app where users sign in with their Hugging Face account? swift-huggingface includes a complete OAuth 2.0 implementation:\n\n```swift\nimport HuggingFace\n\n// Create authentication manager\nlet authManager = try HuggingFaceAuthenticationManager(\n    clientID: \"your_client_id\",\n    redirectURL: URL(string: \"yourapp://oauth/callback\")!,\n    scope: [.openid, .profile, .email],\n    keychainService: \"com.yourapp.huggingface\",\n    keychainAccount: \"user_token\"\n)\n\n// Sign in user (presents system browser)\ntry await authManager.signIn()\n\n// Use with Hub client\nlet client = HubClient(tokenProvider: .oauth(manager: authManager))\n\n// Tokens are automatically refreshed when needed\nlet userInfo = try await client.whoami()\nprint(\"Signed in as: \\(userInfo.name)\")\n```\n\nThe OAuth manager handles token storage in Keychain, automatic refresh, and secure sign-out. No more manual token management.\n\n## Reliable Downloads\n\nDownloading large models is now straightforward with proper progress tracking and resume support:\n\n```swift\n// Download with progress tracking\nlet progress = Progress(totalUnitCount: 0)\n\nTask {\n    for await _ in progress.publisher(for: \\.fractionCompleted).values {\n        print(\"Download: \\(Int(progress.fractionCompleted * 100))%\")\n    }\n}\n\nlet fileURL = try await client.downloadFile(\n    at: \"model.safetensors\",\n    from: \"microsoft/phi-2\",\n    to: destinationURL,\n    progress: progress\n)\n```\n\nIf a download is interrupted, you can resume it:\n\n```swift\n// Resume from where you left off\nlet fileURL = try await client.resumeDownloadFile(\n    resumeData: savedResumeData,\n    to: destinationURL,\n    progress: progress\n)\n```\n\nFor downloading entire model repositories, `downloadSnapshot` handles everything:\n\n```swift\nlet modelDir = try await client.downloadSnapshot(\n    of: \"mlx-community/Llama-3.2-1B-Instruct-4bit\",\n    to: cacheDirectory,\n    matching: [\"*.safetensors\", \"*.json\"],  // Only download what you need\n    progressHandler: { progress in\n        print(\"Downloaded \\(progress.completedUnitCount) of \\(progress.totalUnitCount) files\")\n    }\n)\n```\n\nThe snapshot function tracks metadata for each file, so subsequent calls only download files that have changed.\n\n## Shared Cache with Python\n\nRemember the second problem we mentioned? *\"No shared cache with the Python ecosystem.\"* That's now solved.\n\nswift-huggingface implements a Python-compatible cache structure that allows seamless sharing between Swift and Python clients:\n\n```\n~/.cache/huggingface/hub/\n├── models--deepseek-ai--DeepSeek-V3.2/\n│   ├── blobs/\n│   │   └── <etag>           # actual file content\n│   ├── refs/\n│   │   └── main             # contains commit hash\n│   └── snapshots/\n│       └── <commit_hash>/\n│           └── config.json  # symlink → ../../blobs/<etag>\n```\n\nThis means:\n\n- **Download once, use everywhere.** If you've already downloaded a model with the `hf` CLI or the Python library, swift-huggingface will find it automatically.\n- **Content-addressed storage.** Files are stored by their ETag in the `blobs/` directory. If two revisions share the same file, it's only stored once.\n- **Symlinks for efficiency.** Snapshot directories contain symlinks to blobs, minimizing disk usage while maintaining a clean file structure.\n\nThe cache location follows the same environment variable conventions as Python:\n\n1. `HF_HUB_CACHE` environment variable\n2. `HF_HOME` environment variable + `/hub`\n3. `~/.cache/huggingface/hub` (default)\n\nYou can also use the cache directly:\n\n```swift\nlet cache = HubCache.default\n\n// Check if a file is already cached\nif let cachedPath = cache.cachedFilePath(\n    repo: \"deepseek-ai/DeepSeek-V3.2\",\n    kind: .model,\n    revision: \"main\",\n    filename: \"config.json\"\n) {\n    let data = try Data(contentsOf: cachedPath)\n    // Use cached file without any network request\n}\n```\n\nTo prevent race conditions when multiple processes access the same cache, swift-huggingface uses file locking ([`flock(2)`](https://man7.org/linux/man-pages/man2/flock.2.html)).\n\n## Before and After\n\nHere's what downloading a model snapshot looked like with the old `HubApi`:\n\n```swift\n// Before: HubApi in swift-transformers\nlet hub = HubApi()\nlet repo = Hub.Repo(id: \"mlx-community/Llama-3.2-1B-Instruct-4bit\")\n\n// No progress tracking, no resume, errors swallowed\nlet modelDir = try await hub.snapshot(\n    from: repo,\n    matching: [\"*.safetensors\", \"*.json\"]\n) { progress in\n    // Progress object exists but wasn't always accurate\n    print(progress.fractionCompleted)\n}\n```\n\nAnd here's the same operation with swift-huggingface:\n\n```swift\n// After: swift-huggingface\nlet client = HubClient.default\n\nlet modelDir = try await client.downloadSnapshot(\n    of: \"mlx-community/Llama-3.2-1B-Instruct-4bit\",\n    to: cacheDirectory,\n    matching: [\"*.safetensors\", \"*.json\"],\n    progressHandler: { progress in\n        // Accurate progress per file\n        print(\"\\(progress.completedUnitCount)/\\(progress.totalUnitCount) files\")\n    }\n)\n```\n\nThe API is similar, but the implementation is completely different — built on `URLSession` download tasks with proper delegate handling, resume data support, and metadata tracking.\n\n## Beyond Downloads\n\nBut wait, there's more! swift-huggingface contains a complete Hub client:\n\n```swift\n// List trending models\nlet models = try await client.listModels(\n    filter: \"library:mlx\",\n    sort: \"trending\",\n    limit: 10\n)\n\n// Get model details\nlet model = try await client.getModel(\"mlx-community/Llama-3.2-1B-Instruct-4bit\")\nprint(\"Downloads: \\(model.downloads ?? 0)\")\nprint(\"Likes: \\(model.likes ?? 0)\")\n\n// Work with collections\nlet collections = try await client.listCollections(owner: \"huggingface\", sort: \"trending\")\n\n// Manage discussions\nlet discussions = try await client.listDiscussions(kind: .model, \"username/my-model\")\n```\n\nAnd that's not all! swift-huggingface has everything you need to interact with [Hugging Face Inference Providers](https://huggingface.co/docs/inference-providers/index), giving your app instant access to hundreds of machine learning models, powered by world-class inference providers:\n\n```swift\nimport HuggingFace\n\n// Create a client (uses auto-detected credentials from environment)\nlet client = InferenceClient.default\n\n// Generate images from a text prompt\nlet response = try await client.textToImage(\n    model: \"black-forest-labs/FLUX.1-schnell\",\n    prompt: \"A serene Japanese garden with cherry blossoms\",\n    provider: .hfInference,\n    width: 1024,\n    height: 1024,\n    numImages: 1,\n    guidanceScale: 7.5,\n    numInferenceSteps: 50,\n    seed: 42\n)\n\n// Save the generated image\ntry response.image.write(to: URL(fileURLWithPath: \"generated.png\"))\n```\n\nCheck the [README](https://github.com/huggingface/swift-huggingface) for a full list of everything that's supported.\n\n## What's Next\n\nWe're actively working on two fronts:\n\n**Integration with swift-transformers.** We have a [pull request in progress](https://github.com/huggingface/swift-transformers/pull/297) to replace `HubApi` with swift-huggingface. This will bring reliable downloads to everyone using swift-transformers, [mlx-swift-lm](https://github.com/ml-explore/mlx-swift-lm), and the broader ecosystem. If you maintain a Swift-based library or app and want help adopting swift-huggingface, reach out — we're happy to help.\n\n**Faster downloads with Xet.** We're adding support for the [Xet storage backend](https://huggingface.co/docs/hub/storage-backends), which enables chunk-based deduplication and significantly faster downloads for large models. More on this soon.\n\n## Try It Out\n\nAdd swift-huggingface to your project:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/huggingface/swift-huggingface.git\", from: \"0.4.0\")\n]\n```\n\nWe'd love your feedback. If you've been frustrated with model downloads in Swift, give this a try and [let us know how it goes](https://github.com/huggingface/swift-huggingface/issues). Your experience reports will help us prioritize what to improve next.\n\n## Resources\n\n- [swift-huggingface on GitHub](https://github.com/huggingface/swift-huggingface)\n- [swift-transformers](https://github.com/huggingface/swift-transformers)\n- [mlx-swift-examples](https://github.com/ml-explore/mlx-swift-examples)\n- [AnyLanguageModel](https://github.com/mattt/AnyLanguageModel)\n\n* * *\n\n*Thanks to the swift-transformers community for the feedback that shaped this project, and to everyone who filed issues and shared their experiences. This is for you.* ❤️\n",
  "dateCreated": "2026-10-04T12:50:30.804479567+00:00",
  "date_created": "2025-12-05T00:00:00+00:00",
  "date_modified": "2025-12-05T00:00:00+00:00",
  "date_published": "2025-12-05T00:00:00+00:00",
  "description": "Today, we're announcing swift-huggingface, a new Swift package that provides a complete client for the Hugging Face Hub. You can start using it today as a standalone package, and it will soon integrate into swift-transformers as a…",
  "external_url": "https://huggingface.co/blog/swift-huggingface",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/huggingface-co/2025/12/2025-12-05-introducing-swift-huggingface-the-complete-swift-client-for.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/huggingface-co/2025/12/2025-12-05-introducing-swift-huggingface-the-complete-swift-client-for.md"
  },
  "id": "urn:aggr:item:afd47bca42f9cc065eb34c7292a31339f6fa3345",
  "isBasedOn": {
    "@id": "https://huggingface.co/blog/swift-huggingface"
  },
  "mainEntity": {
    "@id": "https://huggingface.co/blog/swift-huggingface",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2025-12-05-introducing-swift-huggingface-the-complete-swift-client-for/#webpage"
    },
    "dateModified": "2025-12-05T00:00:00+00:00",
    "datePublished": "2025-12-05T00:00:00+00:00",
    "headline": "Introducing swift-huggingface: The Complete Swift Client for Hugging Face",
    "timeRequired": "PT6M",
    "url": "https://huggingface.co/blog/swift-huggingface",
    "wordCount": 1347
  },
  "name": "Archived snapshot: Introducing swift-huggingface: The Complete Swift Client for Hugging Face",
  "preview": null,
  "reading_minutes": 6,
  "replicated_at": null,
  "source": "huggingface-co",
  "source_name": "Hugging Face - Blog",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-04T12:50:30.804479567+00:00",
  "title": "Introducing swift-huggingface: The Complete Swift Client for Hugging Face",
  "url": "https://aggr.aymericbeaumet.com/items/huggingface-co/2025-12-05-introducing-swift-huggingface-the-complete-swift-client-for/",
  "word_count": 1347
}