{"id":32151890,"url":"https://github.com/will-lumley/faviconfinder","last_synced_at":"2025-10-21T10:55:12.897Z","repository":{"id":38707441,"uuid":"228498821","full_name":"will-lumley/FaviconFinder","owner":"will-lumley","description":"A small swift library for iOS \u0026 macOS to detect favicons used by a website.","archived":false,"fork":false,"pushed_at":"2025-09-16T11:04:24.000Z","size":3552,"stargazers_count":200,"open_issues_count":1,"forks_count":35,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-21T10:55:09.874Z","etag":null,"topics":["detect-favicons","favicon","favicon-downloader","ios","linux","macos","swift","swift6"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/will-lumley.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["will-lumley"]}},"created_at":"2019-12-17T00:15:23.000Z","updated_at":"2025-10-09T03:11:54.000Z","dependencies_parsed_at":"2024-01-29T06:40:01.176Z","dependency_job_id":"08fdf8a5-268f-4d15-8836-d3b0ade92fd1","html_url":"https://github.com/will-lumley/FaviconFinder","commit_stats":{"total_commits":139,"total_committers":12,"mean_commits":"11.583333333333334","dds":"0.40287769784172667","last_synced_commit":"a46d1789829f77824990efed552eecd52511d192"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/will-lumley/FaviconFinder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/will-lumley%2FFaviconFinder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/will-lumley%2FFaviconFinder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/will-lumley%2FFaviconFinder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/will-lumley%2FFaviconFinder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/will-lumley","download_url":"https://codeload.github.com/will-lumley/FaviconFinder/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/will-lumley%2FFaviconFinder/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280248570,"owners_count":26297925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-21T02:00:06.614Z","response_time":58,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["detect-favicons","favicon","favicon-downloader","ios","linux","macos","swift","swift6"],"created_at":"2025-10-21T10:55:07.209Z","updated_at":"2025-10-21T10:55:12.888Z","avatar_url":"https://github.com/will-lumley.png","language":"Swift","funding_links":["https://github.com/sponsors/will-lumley"],"categories":[],"sub_categories":[],"readme":"![FaviconFinder: Simple Favicon Finding](https://raw.githubusercontent.com/will-lumley/FaviconFinder/main/FaviconFinder.png)\n\n# FaviconFinder\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/will-lumley/FaviconFinder/actions/workflows/BuildTests-iOS.yml/badge.svg?branch=main\" alt=\"iOS - CI Status\"\u003e\n  \u003cimg src=\"https://github.com/will-lumley/FaviconFinder/actions/workflows/BuildTests-linux.yml/badge.svg?branch=main\" alt=\"macOS - CI Status\"\u003e\n  \u003cimg src=\"https://github.com/will-lumley/FaviconFinder/actions/workflows/BuildTests-macOS.yml/badge.svg?branch=main\" alt=\"Linux - CI Status\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/apple/swift-package-manager\"\u003e\u003cimg src=\"https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat\" alt=\"SPM Compatible\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Swift-5.10-orange.svg\" alt=\"Swift 5.10\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Swift-6.2-orange.svg\" alt=\"Swift 6.2\"\u003e\n  \u003ca href=\"https://bsky.app/profile/will-lumley.bsky.social\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Bluesky-0285FF?logo=bluesky\u0026logoColor=fff\u0026label=will-lumley\" alt=\"Bluesky\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://mastodon.social/@wlumley\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Mastodon-@wlumley-6364FF?logo=mastodon\u0026logoColor=fff\" alt=\"Mastodon\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\nFaviconFinder is a small, pure Swift library designed for iOS, macOS and Linux applications that allows you to detect favicons used by a website.\n\nWhy not just download the file that exists at `https://site.com/favicon.ico`? There are multiple places that a developer can place their favicon, not just at the root directory with the specific filename of `favicon.ico`. The favicon's address may be linked within the HTML header tags, or it may be within a web application manifest JSON file, or it could even be a file with a custom filename.\n\nFaviconFinder handles the dirty work for you and iterates through the numerous possible favicon locations, and simply delivers the image to you once found.\n\nFaviconFinder will:\n\n- [x] Detect the favicon in the root directory of the URL provided.\n- [x] Detect and parse the HTML at the URL for the declaration of the favicon.\n- [x] Resolve the favicon URL for you, even if it's a relative URL to the subdomain that you're querying.\n- [x] Allow you to prioritise which format of favicon you would like served.\n- [x] Detect and parse web application manifest JSON files for favicon locations.\n- [x] Automatically check if the favicon is located within the root URL if the subdomain failed (Will check `https://site.com/favicon.ico` if `https://subdomain.site.com/favicon.ico` fails).\n- [x] If you set `checkForMetaRefreshRedirect` to true, FaviconFinder will analyse the HTML for a meta refresh redirect tag. If such a tag is found, the URL in the tag is the URL that will be queried.\n- [x] Sort favicons by size using the sizeTag metadata (either in HTML or the web app manifest) without downloading the images, allowing you to identify the largest or smallest favicon efficiently.\n- [x] Support pre-fetched HTML documents, so you can reuse HTML you’ve already downloaded instead of fetching it again.\n- [x] Cross-platform support for macOS, macOS Catalyst, iOS, and Linux - and supports SwiftUI, UIKit, and AppKit, ensuring seamless integration across multiple environments and applications.\n\n# Table of Contents\n\n1. [Introduction](#faviconfinder)\n2. [Usage](#usage)\n3. [How it Works](#how-it-works)\n4. [Documentation](#documentation)\n5. [Advanced Usage](#advanced-usage--configuration)\n   - [Preferential Downloading](#preferential-downloading)\n   - [Meta-Refresh Redirects](#meta-refresh-redirects)\n   - [Pre-Fetched HTML](#pre-fetched-html)\n   - [Querying Favicons Behind Authentication](#querying-favicons-behind-authentication)\n   - [Sorting Favicon URLs by Size Without Downloading](#sorting-favicon-urls-by-size-without-downloading)\n6. [Example Projects](#example-projects)\n7. [Requirements](#requirements)\n8. [Installation](#installation)\n   - [Swift Package Manager](#swift-package-manager)\n   - [Cocoapods and Carthage](#cocoapods-and-carthage)\n9. [Author](#author)\n10. [License](#license)\n\n## Usage\n\nFaviconFinder uses simple syntax to allow you to easily download the favicon you need, and get on with your project. Just insert this code into your project.\n\n```swift\n    let favicon = try await FaviconFinder(url: url)\n        .fetchFaviconURLs()\n        .download()\n        .largest()\n\n    self.imageView.image = favicon.image\n```\n\nFaviconFinder will iterate through various sources of where the favicon might live, and will ensure that each source is inspected. Currently, the sources are:\n\n- As a file in the root directory of the URL.\n- Declared in the HTML header\n- Declared in the Web Application Manifest File\n\nOnce FaviconFinder has found all available favicons for a particular source, it will return an array of `FaviconURL`s.\n`FaviconURL` will contain the source URL for the image, and other metadata about the image that existed from where it pulled it from (ie. relevant HTML tags and such).\n\nOn the array of `FaviconURL`s, you can call `download()` which will download each `FaviconURL` and turn your array into an array of `Favicon`s.\n`Favicon` contains the image, its raw data, and the `FaviconURL` property.\n\nOn the array of `Favicon`s, you can call:\n\n- `first()` if you don't care about the size of the favicon, you just want whichever\n- `largest()` if you want the largest favicon we found\n- `smallest()` if you want the smallest favicon we found\n\nFaviconFinder works with UIKit, SwiftUI, AppKit, and macOS Catalyst.\n\nFaviconFinder also supports Linux as a platform, and I have re-implemented parts of FaviconFinder to ensure that Linux is treated as a first-class platform.\nIt's important to note that Swift on Linux doesn't natively support any `Image` format, so when you call download, the `data` itself is downloaded but there's no\nimage type to cast the data to. Also due to this, `largest()` and `smallest()` aren't effective on Linux.\n\n## How it Works\n\nFaviconFinder simplifies the process of locating and retrieving favicons by automating the search through the various places where a favicon can be defined. Since favicons can exist in multiple locations, FaviconFinder systematically queries each potential source, following a priority order that you can customise.\n\n**Key Steps**\n1. **HTML Header Query and Parsing** \u003cbr /\u003e\nFaviconFinder begins by querying the URL you provide. It then inspects the HTML of the webpage and looks for any favicon declarations in the `\u003clink\u003e` or `\u003cmeta\u003e` tags within the header of the downloaded HTML file. This can include favicons specified as standard icons (`\u003clink rel=\"icon\"\u003e`), Apple touch icons (`\u003clink rel=\"apple-touch-icon\"\u003e`), or others defined within the Open Graph metadata.\n\n2.\t**Fallback to the Favicon File** \u003cbr /\u003e\nIf no favicon is found within the HTML, FaviconFinder checks for the traditional favicon location at the root of the domain (https://site.com/favicon.ico). This is the default location where many sites place their favicons, so this check is a quick and effective fallback. If none is found here, FaviconFinder will check if the URL provided is a subdomain (ie. https://example.site.com), and if it is, will query the root domain (ie. https://site.com).\n\n3.\t**Fallback to the Web Application Manifest File** \u003cbr /\u003e\nFor sites that utilise a web application manifest (manifest.json), FaviconFinder parses the JSON file to look for any icons defined specifically for progressive web applications. These are often found in mobile-optimised websites or applications and provide higher-resolution favicons.\n\n4.\t**Meta-Refresh Redirects (Optional)** \u003cbr /\u003e\nSome websites may use meta-refresh redirects instead of server-side HTTP redirects. If enabled in the configuration, FaviconFinder will inspect the HTML for these meta-refresh redirects and follow them to retrieve the favicon from the redirected URL.\nIt's worthwhile to note that FaviconFinder will prevent itself from falling into a recursive loop if a redirect directs to a site that then redirects to the original site.\n\n5.\t**Favicon Size Sorting** \u003cbr /\u003e\nFaviconFinder extracts size metadata from the HTML or web application manifest to sort favicons by their dimensions (e.g., 120x120, 32x32). This allows you to easily determine the largest or smallest favicon without downloading every image, saving bandwidth and improving performance.\n\n6.\t**Customisation and Preferences** \u003cbr /\u003e\nFaviconFinder allows you to customise how it searches for favicons. You can prioritise certain favicon types (e.g., Apple touch icons, .ico files) and even provide pre-fetched HTML or custom HTTP headers for authentication, giving you full control over how the library interacts with the site.\n\n7.\t**Cross-Platform Compatibility** \u003cbr /\u003e\nFaviconFinder is designed to work across macOS, iOS, and Linux. It adjusts its methods depending on the platform, so you can use it seamlessly whether you’re working in SwiftUI, UIKit, or AppKit. On Linux, FaviconFinder ensures compatibility even though the platform lacks native image handling, using data-driven methods instead.\n\n## Documentation\n\nWhile this README provides a basic rundown of FaviconFinder, how to use it and what it can do, a much more thorough documentation can be found here:\n\nhttps://will-lumley.github.io/FaviconFinder/documentation/faviconfinder/\n\n## Advanced Usage \u0026 Configuration\n\n### Preferential Downloading\n\nIf you want fine-tuned control over the type of favicon you’re looking for, FaviconFinder lets you specify your preferences with ease. You can choose which favicon source you’d prefer to query first—whether it’s HTML, an actual file, or a web application manifest file—and even specify which favicon type you’d like for each source.\n\nFor example, you might prefer a favicon declared in the HTML header and specifically want the appleTouchIcon type. FaviconFinder will search the HTML for that specific tag, but if it’s not found, it will automatically search for other HTML favicon types.\n\nIf a specified download type (e.g., HTML or .ico) isn’t available, FaviconFinder will automatically try other available methods, and if none are found, it will return an error.\n\nYou can also request that FaviconFinder checks for meta-refresh redirects in the HTML, allowing it to follow any redirects and query the correct URL.\n\nHere’s an example of how to configure your preferences:\n\n```swift\n    let favicon = try await FaviconFinder(\n        url: url,\n        configuration: .init(\n            preferredSource: .html,\n            preferences: [\n                .html: FaviconFormatType.appleTouchIcon.rawValue,\n                .ico: \"favicon.ico\",\n                .webApplicationManifestFile: FaviconFormatType.launcherIcon4x.rawValue\n            ]\n        )\n    )\n        .fetchFaviconURLs()\n        .download()\n        .largest()\n\n    self.imageView.image = favicon.image\n```\n\nThis allows you to control:\n- The preferred download source type FaviconFinder should use first.\n- The specific sub-type to prioritize within each download type (e.g., HTML rel types, filenames for .ico files, or keys in the web application manifest file).\n\nIf your preferred download type isn’t available at the URL (e.g., there’s no file or no HTML tag specifying the favicon), FaviconFinder will try other sources until it finds a valid favicon or returns an error.\n\n### Meta-Refresh Redirects\n\nWhen a site is moved from oldsite.com to newsite.com, it's common practice to have oldsite.com respond with a HTTP 301 Redirect, along with a URL to redirect to.\nIn this example, `URLSession` (and by extension most libraries that fetch favicons) will natively re-request newsite.com once the HTTP 301 redirect is received.\n\nHowever there is a lesser-practiced (and frankly inferior) method of redirecting - and it's called the meta-refresh redirect.\n\nThis is similar to the HTTP 301 Redirect, except it occurs in the front-end and the browser is expected to read \u0026 parse the HTML and send the user to the new URL that way.\nWhen `URLSession` encounters a HTTP request that points to a HTML file that contains a meta-refresh redirect, nothing happens.\n\nHowever with `FaviconFinder` you're in luck. If you set it to do so within the configuration, `FaviconFinder` will scan the HTML at the URL you provide it for any meta-refresh redirects to make sure that if a\nmeta-refresh redirect is encountered, you don't have to worry about it.\n\nIt's important to note however that parsing and checking for this can take extra compute time, so by default it's set to off.\n\nHere is how you would use it.\n\n```swift\n    let favicon = try await FaviconFinder(\n        url: url,\n        configuration: .init(\n            preferredSource: .html,\n            preferences: [\n                .html: FaviconFormatType.appleTouchIcon.rawValue,\n                .ico: \"favicon.ico\",\n                .webApplicationManifestFile: FaviconFormatType.launcherIcon4x.rawValue\n            ],\n            checkForMetaRefreshRedirect: true\n        )\n    )\n        .fetchFaviconURLs()\n        .download()\n        .largest()\n\n    self.imageView.image = favicon.image\n```\n\n### Pre-Fetched HTML\n\nFaviconFinder allows you to pass a pre-fetched HTML document to avoid downloading the HTML multiple times or if you already have the HTML content available from another source. You can use the prefetchedHTML property in the configuration to pass this document.\n\nWe use the Document type of SwiftSoup, as it has amazing HTML storing and parsing capabilities, allowing for easy manipulation and traversal of the document tree.\n\nThis feature is useful when:\n\n- You have already downloaded the HTML document elsewhere in your app and want to reuse it.\n- You’re working with local HTML files or custom documents.\n- You want to optimise performance by reducing the number of HTTP requests.\n\nHere's how you can use it:\n\n```swift\nimport SwiftSoup\n\n// Assuming you have already fetched and parsed the HTML\nlet htmlString = \"\u003chtml\u003e\u003chead\u003e...\u003c/head\u003e\u003c/html\u003e\"\nlet document = try SwiftSoup.parse(htmlString)\n\nlet favicon = try await FaviconFinder(\n    url: url,\n    configuration: .init(prefetchedHTML: document)\n)\n    .fetchFaviconURLs()\n    .download()\n    .largest()\n\nself.imageView.image = favicon.image\n```\n\n### Querying Favicons Behind Authentication\n\nIn some cases, favicons might be stored behind an authentication layer or require custom HTTP headers to access, such as API tokens or cookies for user sessions. FaviconFinder supports querying favicons using custom HTTP headers, allowing you to fetch favicons even if they require authentication.\n\nTo specify custom HTTP headers, you can pass them in the Configuration object when initializing FaviconFinder. These headers will be sent with every HTTP request FaviconFinder makes, ensuring that the favicon can be accessed even if it’s behind an authentication layer.\n\nBelow is an example of how to set up a request using custom HTTP headers to access favicons that require authentication (e.g., using a bearer token):\n\n```swift\nlet headers = [\n    \"Authorization\": \"Bearer your_token_here\",\n    \"Cookie\": \"session_id=your_session_id_here\"\n]\n\nlet favicon = try await FaviconFinder(\n    url: url,\n    configuration: .init(\n        httpHeaders: headers\n    )\n)\n    .fetchFaviconURLs()\n    .download()\n    .largest()\n\nself.imageView.image = favicon.image\n```\n\nBy providing custom HTTP headers, you can handle more advanced scenarios where favicons are restricted or protected behind security layers, ensuring that FaviconFinder remains flexible and functional in a variety of environments.\n\n### Sorting Favicon URLs by Size Without Downloading\n\nFaviconFinder includes functionality that allows you to determine the largest or smallest favicon available without needing to download all the images first. This is useful for optimising performance when you only need the largest or smallest image based on size metadata.\n\nBy inspecting the size tags provided in the HTML or web application manifest file, FaviconFinder can sort through the available favicon URLs and select the one with the largest or smallest dimensions.\n\nTo find the largest or smallest favicon URL without downloading the images:\n\n```swift\nlet faviconURL = try await FaviconFinder(url: url)\n    .fetchFaviconURLs()\n    .largest()  // or .smallest()\n\nprint(\"Largest Favicon URL: \\(faviconURL.source)\")\n```\n\nThis will return the largest or smallest favicon based on the metadata in the size tag.\n\nOnce you have identified the largest or smallest favicon URL, you can then proceed to download the actual image:\n\n```swift\nlet largestFavicon = try await FaviconFinder(url: url)\n    .fetchFaviconURLs()\n    .largest()\n    .download()\n\nself.imageView.image = largestFavicon.image\n```\n\nThere are pros and cons to using this approach.\n\nAdvantages\n\n- No need to download all the images to determine which one is largest or smallest.\n- Optimises performance by focusing on the favicons that meet your size requirements.\n\nLimitations\n\n- The sizeTag metadata must be accurately set by the source (HTML or web app manifest).\n- The actual image size may differ if the source configuration is incorrect.\n\nThis functionality allows you to efficiently sort favicon URLs by size and download only the favicons you need, making it a powerful tool for handling favicons in an optimised manner.\n\n### Header \u0026 Hero Images\n\nThere are times where you would like to fetch the header or hero image from a URL. You can let FaviconFinder know that you want it to do this with the `acceptHeaderImage` parameter in the configuration.\nWith the default value set to `false`, this parameter is an opt-in one.\n\nYou can use it as so:\n\n```swift\nlet favicon = try await FaviconFinder(\n    url: url,\n    configuration: .init(acceptHeaderImage: true)\n)\n    .fetchFaviconURLs()\n    .download()\n    .largest()\n```\n\n## Example Projects\n\nTo run the example project, clone the repo, and open the example Xcode Project in either the `iOSFaviconFinderExample`, or `macOSFaviconFinderExample`, depending on your build target.\n\nAlternatively, if you're using this for a Linux project, you can open the example Swift Project located in `LinuxFaviconFinderExample`.\n\n## Requirements\n\nFaviconFinder is now written with Swift 6. This means we get to use `Swift Testing` over `XCTest`, but more importantly means FaviconFinder is now data-race safe and adheres to strict concurrency.\n\nSwift 6 is supported from version `5.1.0` and up. If you need FaviconFinder in Swift 5.9 and below, please use version `5.0.4`.\n\nFaviconFinder now supports await/async concurrency, as seen in the examples below. Due to this, the most up to date version of FaviconFinder requires iOS 15.0 and macOS 12.0.\nIf you need to support older versions of iOS or macOS, version `3.3.0` of FaviconFinder uses closures to call back the success/failure instead of await/async concurrency.\n\n## Installation\n\n### Swift Package Manager\n\nFaviconFinder is available through [Swift Package Manager](https://github.com/apple/swift-package-manager).\nTo install it, simply add the dependency to your Package.Swift file:\n\n```swift\n...\ndependencies: [\n    .package(url: \"https://github.com/will-lumley/FaviconFinder.git\", from: \"5.1.5\"),\n],\ntargets: [\n    .target( name: \"YourTarget\", dependencies: [\"FaviconFinder\"]),\n]\n...\n```\n\n### Cocoapods and Carthage\n\nFaviconFinder was previously available through CocoaPods and Carthage, however making the library available to all three, Cocoapods,\nCarthage, and SPM (and functional to all three) was becoming troublesome. This, combined with the fact that SPM has seen a serious\nup-tick in adoption \u0026 functionality, has led me to remove support for CocoaPods and Carthage.\n\n## Author\n\n[William Lumley](https://lumley.io/), will@lumley.io\n\n## License\n\nFaviconFinder is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwill-lumley%2Ffaviconfinder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwill-lumley%2Ffaviconfinder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwill-lumley%2Ffaviconfinder/lists"}