Skip to content

Repository files navigation

The PC icon Finder draws for a host without a model identifier

Device Icons CI Release License Buy Me A Coffee

Any host on your network can show up in Finder with the icon of an Apple device. A Raspberry Pi or a NAS will do.

The host announces itself over Bonjour, and its _device-info._tcp record carries a model value. Finder draws the icon for that model. For example, model=MacPro7,1 gives the 2019 Mac Pro tower.

Two commands help you choose a model identifier:

  • icons export writes every icon macOS has for a model identifier and groups them, so you can pick one by eye in Finder.
  • icons preview shows the icon for a model identifier in Finder's Network view. You don't need the device.

A third, symbols export, writes the SF Symbol of each device type as SVG, for a project that draws devices itself.

All three need macOS 13 or later. The icons live in CoreTypes.bundle, the symbols in CoreGlyphs.bundle, and iconutil, osascript, open, and dns-sd do work that no Python module does. You need uv; the tool itself uses only the standard library.

Run the commands from a checkout with uv run device-icons, or without a checkout:

uvx --from git+https://github.com/bkahlert/device-icons device-icons --help

One call of each:

uvx --from git+https://github.com/bkahlert/device-icons device-icons icons export
uvx --from git+https://github.com/bkahlert/device-icons device-icons icons preview MacPro7,1
uvx --from git+https://github.com/bkahlert/device-icons device-icons symbols export

Once you have chosen a model identifier, set it as model=<identifier> in your host's _device-info._tcp record. Pi Hero does that for a Raspberry Pi.

Finder's Network view showing the eleven devices of the table below

icons export

Here are eleven of the icons, laid out by icons export --horizontal:

Model identifier AirPort4 AirPort5 AirPort7,120 Macmini8,1 Macmini9,1 MacPro5,1 MacPro6,1 AirPort6 Xserve3,1 MacPro7,1
@ECOLOR=
225,225,223
MacPro7,1
@ECOLOR=
226,226,224
Type identifier com.apple.airport-express com.apple.airport com.apple.airport-extreme-tower com.apple.macmini-2018 com.apple.macmini-2020 com.apple.macpro-firewire com.apple.macpro-cylinder com.apple.time-capsule com.apple.xserve-xeon com.apple.macpro-2019 com.apple.macpro-2019-rackmount
Kind Mac AirPort Extreme AirPort Extreme Mac Mac Mac Mac Time Capsule Mac Mac Mac
Icon com.apple.airport-express com.apple.airport-extreme com.apple.airport-extreme-tower com.apple.macmini-2018 com.apple.macmini-2020 com.apple.macpro com.apple.macpro-cylinder com.apple.time-capsule com.apple.xserve com.apple.macpro-2019 com.apple.macpro-2019-rackmount
Sidebar icon SidebarAirportExpress SidebarAirportExtreme SidebarAirportExtremeTower SidebarMacMini SidebarMacMini SidebarMacPro SidebarMacProCylinder SidebarTimeCapsule SidebarXserve com.apple.macpro-2019 com.apple.macpro-2019-rackmount

To get every icon:

uv run device-icons icons export

icons export reads each model identifier declared in CoreTypes.bundle and asks LaunchServices which type it resolves to. It then writes that type's icon and sidebar icon, grouped by sidebar icon:

Path Content
icons/<icon>.png the largest image of each icon file, written once
sidebar/<sidebar>.png each sidebar icon at 64 px, written once; named after its Sidebar….icns, or after its icon file when embedded there
by-sidebar/<sidebar>/ one folder per sidebar icon, with that sidebar icon as the folder's icon
by-sidebar/<sidebar>/<icon>.png a link to icons/<icon>.png for every icon that comes with that sidebar icon
index.json sidebars lists each sidebar icon, its icons, and their type and model identifiers; dropped lists the model identifiers left out, by reason
README.md the same data as a table, one row per model identifier: type identifier, Kind, icon, sidebar icon

The output goes to out/, or to the directory you name, and opens in Finder. icons export creates the directory if needed.

If the directory already holds an earlier run, icons export replaces it. Anything else in it makes it stop, so it never deletes your files. It recognises an earlier run by its file names and the first line of its README.md.

Pick an icon

Open by-sidebar/ in Finder. Each folder shows a sidebar icon as its folder icon and holds the icons that come with it.

Once you have picked an icon, look it up in index.json. It lists the model identifiers that produce the icon, and any of them works as model=…:

{
  "sidebars": {
    "SidebarXserve": {
      "sidebar_icon": "sidebar/SidebarXserve.png",
      "icons": {
        "com.apple.xserve": {
          "icon": "icons/com.apple.xserve.png",
          "type_identifiers": ["com.apple.xserve", "com.apple.xserve-xeon", "com.apple.mac.rackmount"],
          "model_identifiers": ["RackMac", "RackMac1,1", "Xserve", "Xserve3,1"]
        }
      }
    }
  }
}

dropped, next to sidebars, lists what icons export left out and why. Displays are left out because their types are no devices. Apple TV, Watch, and AirPods are left out because their types have no sidebar icon.

Check the result with icons preview.

A few icons

To write only the icons of your project's devices into its docs, without opening Finder:

uv run device-icons icons export --no-open --model MacPro7,1 --model Xserve3,1 docs/icons

To write whole types instead:

uv run device-icons icons export --no-open --type com.apple.macpro-2019 --type com.apple.xserve-xeon docs/icons

--model takes the model identifiers you name and the types they resolve to. --type takes the type identifiers you name and the model identifiers that resolve to them. You can't combine the two.

icons export stops before it writes anything if an identifier is not declared or resolves to no type. It also stops if the type has no icon or no sidebar icon. The message names the identifier and says what is missing.

A table for a README

--horizontal turns the table in README.md on its side. It gets one column per model identifier, and one row each for type identifier, Kind, icon, and sidebar icon. The table at the top of this section was made this way:

uv run device-icons icons export --horizontal --no-open --model MacPro7,1 --model Xserve3,1 docs/icons

icons preview

To see the icon Finder draws for a model identifier, without owning the device:

uv run device-icons icons preview MacPro7,1

Finder's Network view opens. Within a few seconds a device named MacPro7,1 appears, with the icon that identifier produces, as pictured above.

icons preview keeps the device there until you press Ctrl-C, it receives a termination signal, or one of its registrations ends. Then it unregisters.

To compare several at once:

uv run device-icons icons preview MacPro7,1 Xserve3,1 "Mac14,8@ECOLOR=1"

To use the name your real device will have, pass --name. It takes one model identifier at a time, because Finder pairs a device's records by name:

uv run device-icons icons preview --name "Rack" MacPro7,1@ECOLOR=226,226,224

--no-open doesn't open Finder. Open the Network view yourself with Go > Network or ⇧⌘K.

icons preview can't show the sidebar icon. Finder shows it only under Locations, for a server it has mounted, and the previewed host does not exist.

symbols export

Here are the symbols of ten of the eleven devices above, laid out by symbols export --horizontal; the Xserve's type declares no symbol name:

Model identifier AirPort4 AirPort5 AirPort6 AirPort7,120 Macmini8,1 Macmini9,1 MacPro5,1 MacPro6,1 MacPro7,1
@ECOLOR=
225,225,223
MacPro7,1
@ECOLOR=
226,226,224
Type identifier com.apple.airport-express com.apple.airport com.apple.time-capsule com.apple.airport-extreme-tower com.apple.macmini-2018 com.apple.macmini-2020 com.apple.macpro-firewire com.apple.macpro-cylinder com.apple.macpro-2019 com.apple.macpro-2019-rackmount
Symbol name airport.express airport.extreme airport.extreme airport.extreme.tower macmini.gen2 macmini.gen2 macpro.gen1 macpro.gen2 macpro.gen3 macpro.gen3.server
Symbol airport.express airport.extreme airport.extreme airport.extreme.tower macmini.gen2 macmini.gen2 macpro.gen1 macpro.gen2 macpro.gen3 macpro.gen3.server

To write the SF Symbol of every device type as SVG:

uv run device-icons symbols export

symbols export resolves each model identifier declared in CoreTypes.bundle to its type, as icons export does, takes the type's symbol name, and reads the symbol from CoreGlyphs.bundle through CoreUI, the framework Finder draws it with. It writes:

Path Content
symbols/<symbol name>.svg the symbol at regular weight and medium scale under its current name, written once: a tight viewBox, a path filled with currentColor per layer, no width or height
index.json symbols lists each symbol name, its file, and the type and model identifiers that get it; types lists each type identifier's description, symbol name, and model identifiers; dropped lists the model identifiers left out, by reason
README.md the same data as a table, one row per model identifier: type identifier, symbol name, symbol

The output goes to out/, or to the directory you name, and opens in Finder. An earlier run in that directory is replaced; anything else in it makes it stop, as with icons export.

A type without a symbol name or description takes the one of the nearest type it conforms to, the way it takes an icon file: com.apple.macpro-2019 gets both from com.apple.macpro, macpro.gen3 and Mac Pro, and index.json lists them under types. Whether Finder inherits symbol names the same way is not verified. Most model identifiers get a symbol this way; the Power Macs and Xserves are among those that don't. A legacy symbol name such as visionpro is followed to its current name, vision.pro, through the bundle's name_aliases.strings, as NSImage does; the file and the index.json entry carry the current name. dropped names the model identifiers left out: no type for one no type declares, no symbol name for one whose type neither declares nor inherits a symbol name, and no symbol for one whose symbol name CoreGlyphs.bundle doesn't have, which happens for a private name or two.

A symbol is drawn in its preferred rendering mode, as NSImage draws it by default: hierarchical for most device symbols, monochrome for about two in five, the Mac Pro and the Apple TV among them. Hierarchical gives each layer the opacity of its level, 1 for primary, 0.5 for secondary, 0.3 for tertiary, so the screen of an iPad is a translucent layer under its frame; monochrome draws every layer at 1. The two values are AppKit's; --secondary and --tertiary set others between 0 and 1, say --tertiary 0.25 for what the SF Symbols app's Copy Image as SVG draws. An eraser layer, which the AirPods Pro use to cut the bud behind the ear tip, is cut out of the layers before it, so every path is the shape you see. An eraser with an opacity of its own, as the arms of snowflake are, is drawn as well; a group of layers acts as one, its shape their compound.

All SVGs share one unit, so the viewBox carries each symbol's size relative to the others: the Mac Pro is 101 by 123, the iPhone 63 by 103. Render them at a common scale to keep that, or let each fill its box.

{
  "symbols": {
    "macpro.gen3": {
      "symbol": "symbols/macpro.gen3.svg",
      "type_identifiers": ["com.apple.macpro", "com.apple.macpro-2019", "com.apple.macpro-2023"],
      "model_identifiers": ["Mac14,8", "Mac14,8@ECOLOR=0", "MacPro", "MacPro7,1", "MacPro7,1@ECOLOR=225,225,223"]
    }
  },
  "types": {
    "com.apple.macpro-2019": {
      "description": "Mac Pro",
      "symbol_name": "macpro.gen3",
      "model_identifiers": ["MacPro7,1", "MacPro7,1@ECOLOR=225,225,223"]
    }
  }
}

A few symbols

--symbol takes symbol names, any SF Symbol, declared by a device type or not:

uv run device-icons symbols export --no-open --symbol macpro.gen3 --symbol xserve.raid docs/symbols

A symbol no model identifier gets still has its row, with the identifier cells empty, so you can look at it. --horizontal turns the table on its side as it does for icons export.

--symbol takes a legacy name as well: visionpro and vision.pro both write vision.pro.svg, with the same model identifiers.

--model takes model identifiers instead, as it does for icons export, and writes their symbols with those model identifiers only. You can't combine it with --symbol.

symbols export stops before it writes anything if a symbol name is not in CoreGlyphs.bundle, or if a model identifier is not declared, resolves to no type, or gets no symbol. The message names the identifier and says what is missing.

To fetch fresh symbols into another project without a checkout:

uvx --from git+https://github.com/bkahlert/device-icons device-icons symbols export --no-open path/to/symbols

The symbols are Apple's; see License for what their agreement allows.

Development

Install the dependencies:

uv sync

Run the tests:

uv run pytest

Lint and format, as CI does:

uv run ruff check && uv run ruff format --check

Run the tool from the checkout:

uv run device-icons --help

CI runs the tests and ruff on macOS 15 and macOS 26. It runs on every push and pull request, and every Monday. The Monday run catches a macOS update that moves an icon in CoreTypes.bundle or changes CoreUI, the private framework symbols export reads symbols through, without waiting for a push. main only accepts pull requests with green checks.

The package is in src/device_icons/, the tests in tests/. Logic that needs no macOS, such as reading type declarations and building the index, is tested on fixtures. The tests replace dns-sd and open with fakes. Code that calls iconutil, osascript, or CoreUI is tested on macOS only.

Icon lookup

This is how Finder turns a model identifier into an icon. icons export does the same.

  • A model identifier is a tag of the tag class com.apple.device-model-code. It sits in a type declaration of CoreTypes.bundle or of a bundle nested in its Contents/Library, such as MobileDevices.bundle.
  • Several types claim about half the model identifiers, mostly colour variants of one device. LaunchServices picks the winner. icons export asks the way Finder does, for the preferred type identifier of the tag that conforms to public.device.
  • If no type claims a model identifier, it resolves to a dynamic dyn.* type and Finder shows a question mark. The same happens for a display, because its type conforms to public.display, not public.device. That is why AppleDisplay2,1 and AppleDisplay18,2 are the "no type" rows of a full run.
  • A type's icon is its icon file. A type without one inherits the icon file of the nearest parent along UTTypeConformsTo.
  • The sidebar icon comes from one of two places. The first is the Sidebar….icns file a type names in _UTTypeTemplateIconFile. The second is the sbtp chunk that newer icon files embed, which iconutil unpacks as template_… images. icons export prefers the embedded one. Which one Finder prefers when a type has both is not verified.
  • A type's symbol is the SF Symbol its symbol name, UTTypeSymbolName, names. It lives in CoreGlyphs.bundle, in nine weights and three scales, and Finder draws it through CoreUI. symbols export asks CoreUI for regular weight and medium scale, and lets a type without a symbol name inherit its nearest parent's, as it does for icon files. Whether Finder inherits symbol names is not verified. CoreUI knows a symbol by its current name only; CoreGlyphs.bundle maps legacy names to current ones in name_aliases.strings, and symbols export follows that map as NSImage does. A symbol's CGPath in CoreUI concatenates its monochrome layers, erasers included, and loses the levels; symbols export reads the layers of the preferred rendering mode one by one instead, and cuts an eraser out of the layers before it with CGPathCreateCopyBySubtractingPath.
  • Finder's Network view draws the icon. The sidebar icon appears only under Locations, for a server that is mounted.

icons preview works the other end of this. For each model identifier it registers two proxy records from your Mac: an _smb._tcp service, and a _device-info._tcp service with model=<identifier>. Both use the same service instance name, which defaults to the identifier. Finder pairs them by that name and draws the icon for the model.

Glossary

These are Apple's terms, checked against the keys in Info.plist, the tools' manuals, and the tools' output. Code and output use them as written here, or in the short form given. Where the language needs it, they are written in snake_case.

Term Example Meaning
Model identifier MacPro7,1 The name About This Mac shows and sysctl hw.model prints. In CoreTypes.bundle it is a tag of the tag class com.apple.device-model-code. In a _device-info._tcp TXT record it is the value of model. It may carry an enclosure colour: MacPro7,1@ECOLOR=226,226,224.
Board name J120AP The name sysctl hw.target prints. MobileDevices.bundle declares board names as model identifiers of iPhones and iPads, and LaunchServices resolves them like any other tag.
Type identifier com.apple.macpro-2019 A Uniform Type Identifier, the UTTypeIdentifier of a type declaration. It is case-insensitive. LaunchServices returns com.apple.ipad-pro-a1670-1 for the declared com.apple.ipad-pro-A1670-1. Short form: type.
Type declaration One entry of UTExportedTypeDeclarations in a bundle's Info.plist.
Tag class, tag com.apple.device-model-code, MacPro7,1 UTTypeTagSpecification maps tag classes to the tags a type claims. Other tag classes are public.filename-extension and public.mime-type.
Conforms to com.apple.macpro, com.apple.mac.tower UTTypeConformsTo names the parent types. A type without a description, icon, sidebar icon, or Kind of its own takes them from the nearest parent.
Preferred type identifier com.apple.macpro-2019 for MacPro7,1 The one type identifier LaunchServices returns for a tag that several declarations claim, via UTTypeCreatePreferredIdentifierForTag. An unclaimed tag gets a dynamic type, dyn.….
Bundle, bundle identifier CoreTypes.bundle, com.apple.coretypes A bundle is the directory; its CFBundleIdentifier is the bundle identifier. Device types live in /System/Library/CoreServices/CoreTypes.bundle and in the bundles nested in its Contents/Library.
Kind Mac, iPad, Time Capsule Finder's Kind column in the Network view. It shows iPhone, iPad, iPod, AirPort Extreme, or Time Capsule for a type that is or conforms to com.apple.iphone, .ipad, .ipod, .airport, or .time-capsule. It shows Mac for every other model, Apple TV and Watch included, and PC for a host without one. Observed, not documented.
Description Mac Pro UTTypeDescription, the name a type declaration gives its type, written as it is, iPhone X (Model A1865, A1901, A1902, A1903) for one. Few device types declare one; symbols export lets the others inherit the nearest parent's, as with symbol names. Finder's Kind is coarser.
Icon com.apple.macpro-2019.icns The picture Finder draws for a type. It comes from the type's icon file, UTTypeIconFile, an .icns in the bundle's Contents/Resources that holds it at several sizes.
Sidebar icon SidebarMacPro.icns The monochrome icon Finder's sidebar draws under Locations. It is either the Sidebar….icns file a type names in _UTTypeTemplateIconFile, or the sbtp chunk embedded in its icon file. Short form: sidebar.
Template image template_32x32@2x.png A monochrome image the system tints. AppKit calls this isTemplate. It describes how a sidebar icon is rendered, not what it is. iconutil names an embedded sidebar icon's images template_….
Iconset icon_512x512@2x.png The folder iconutil -c iconset unpacks an icon file into: one PNG per image, named by point size and scale.
Symbol name macpro.gen3 UTTypeSymbolName, the SF Symbol of a type. Few device types declare one; symbols export lets the others inherit the nearest parent's. Some are legacy names, which name_aliases.strings maps to current ones; symbols export uses the current name.
Symbol symbols/macpro.gen3.svg An SF Symbol: the layered vector shape CoreGlyphs.bundle holds under a symbol name. symbols export writes it as SVG in its preferred rendering mode, a path filled with currentColor per layer in a tight viewBox.
Layer hierarchical-1:tertiary One of the shapes a symbol is drawn from, in order. In hierarchical rendering a layer has a level, primary, secondary, or tertiary, that sets its opacity. An eraser layer cuts its shape out of the layers before it, and is drawn too if it has an opacity; symbols export writes the layers already cut.
Rendering mode hierarchical How a symbol's layers are colored. Monochrome draws every layer in one color; hierarchical draws each in that color at its level's opacity, 1, 0.5, or 0.3 unless --secondary or --tertiary say otherwise. Each symbol prefers one mode, which NSImage uses by default and symbols export always. Multicolor and palette are not written.
Outline Outline(layers, bounds) A symbol's layers as paths at one point size, weight, and scale, with the bounds of what they draw. Typography's word for the shape of a glyph; symbols export reads one per symbol from CoreUI, layer by layer.
Weight, scale regular, medium SF Symbols terms. The nine weights run from ultralight to black, as font weights do. The three scales, small, medium and large, size a symbol next to text of one point size. Finder's defaults are regular and medium.
Asset catalog Assets.car The compiled catalog CoreUI reads. CoreGlyphs.bundle keeps the symbols in Contents/Resources/Assets.car, next to CoreTypes.bundle in /System/Library/CoreServices.
Service type _device-info._tcp, _smb._tcp A DNS-SD service type (RFC 6763). Finder reads model from _device-info._tcp.
Service instance name MacPro7,1 in dns-sd -P MacPro7,1 … The name of one instance of a service type. Finder pairs the _device-info._tcp record with the _smb._tcp record by it.
TXT record model=MacPro7,1 The key-value pairs of a service instance.
Proxy registration dns-sd -P Registering a service on behalf of another host, with its host name and address.
Network view Go > Network, ⇧⌘K Finder's list of the servers on the local network. open on the Network.app inside Finder.app/Contents/Applications shows it. The /Network folder of earlier macOS is gone.

Release

Bump the version on a branch:

uv version --bump minor

Merge it like any other change, then tag main:

git tag v0.2.0 main && git push origin v0.2.0

The tag must match the version in pyproject.toml. CI then tests the code, builds the wheel and the source distribution, and publishes them as a GitHub release with generated notes. A version with a pre-release marker, such as 0.2.0rc1, becomes a pre-release.

Contributing

Star the project or raise issues. A PayPal donation helps too.

License

MIT covers the code. See LICENSE.

The icons and symbols the tool writes are Apple's. This project exists for educational purposes: it shows how Finder turns a model identifier into an icon or a symbol. Apple licenses its system-provided images, SF Symbols included, solely for developing applications for Apple-branded products, and forbids their use in app icons, logos, or as trademarks; see section 2.10, System-Provided Images, of the Xcode and Apple SDKs Agreement. Using the images on other platforms is not allowed under that agreement.

About

Dump the Finder icons of Apple device types and preview model identifiers in Finder

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages