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 exportwrites every icon macOS has for a model identifier and groups them, so you can pick one by eye in Finder.icons previewshows 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 --helpOne 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 exportOnce 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.
Here are eleven of the icons, laid out by icons export --horizontal:
To get every icon:
uv run device-icons icons exporticons 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.
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.
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/iconsTo 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.
--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/iconsTo see the icon Finder draws for a model identifier, without owning the device:
uv run device-icons icons preview MacPro7,1Finder'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.
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:
To write the SF Symbol of every device type as SVG:
uv run device-icons symbols exportsymbols 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"]
}
}
}--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/symbolsA 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/symbolsThe symbols are Apple's; see License for what their agreement allows.
Install the dependencies:
uv syncRun the tests:
uv run pytestLint and format, as CI does:
uv run ruff check && uv run ruff format --checkRun the tool from the checkout:
uv run device-icons --helpCI 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.
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 ofCoreTypes.bundleor of a bundle nested in itsContents/Library, such asMobileDevices.bundle. - Several types claim about half the model identifiers, mostly colour variants of one device. LaunchServices picks the
winner.
icons exportasks the way Finder does, for the preferred type identifier of the tag that conforms topublic.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 topublic.display, notpublic.device. That is whyAppleDisplay2,1andAppleDisplay18,2are 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….icnsfile a type names in_UTTypeTemplateIconFile. The second is thesbtpchunk that newer icon files embed, whichiconutilunpacks astemplate_…images.icons exportprefers 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 inCoreGlyphs.bundle, in nine weights and three scales, and Finder draws it through CoreUI.symbols exportasks 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.bundlemaps legacy names to current ones inname_aliases.strings, andsymbols exportfollows that map asNSImagedoes. A symbol'sCGPathin CoreUI concatenates its monochrome layers, erasers included, and loses the levels;symbols exportreads the layers of the preferred rendering mode one by one instead, and cuts an eraser out of the layers before it withCGPathCreateCopyBySubtractingPath. - 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.
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. |
Bump the version on a branch:
uv version --bump minorMerge it like any other change, then tag main:
git tag v0.2.0 main && git push origin v0.2.0The 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.
Star the project or raise issues. A PayPal donation helps too.
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.