Last active
March 14, 2026 19:28
-
-
Save insidegui/686ae55087542b430be976918418d129 to your computer and use it in GitHub Desktop.
A SwiftUI view that renders a Mac app's icon in the current appearance, respecting Liquid Glass customizations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| #if canImport(WidgetKit) | |
| import WidgetKit | |
| #endif | |
| /// Displays the app's icon in the current appearance (light, dark, clear, etc). | |
| /// | |
| /// - note: The appearance of the icon will only reflect what's currently selected in System Settings, | |
| /// it will not update in SwiftUI previews or if the app is overriding its own appearance. | |
| public struct AppIconView: View { | |
| public var size: CGFloat | |
| @State private var provider = _Provider() | |
| public init(size: CGFloat = 128) { | |
| self.size = size | |
| } | |
| @Observable | |
| final class _Provider { | |
| private(set) var icon = NSImage.appIcon | |
| @ObservationIgnored private var iconAppearanceObservation: Any? | |
| @ObservationIgnored private var appAppearanceObservation: NSKeyValueObservation? | |
| init() { | |
| /// Refresh icon image when configuration changes. | |
| iconAppearanceObservation = NSWorkspace.shared.notificationCenter.addObserver(forName: .iconAppearanceConfigurationDidChange, object: nil, queue: .main) { [weak self] _ in | |
| self?.didChange() | |
| } | |
| /// Refresh icon image when app appearance changes. | |
| appAppearanceObservation = NSApplication.shared.observe(\.effectiveAppearance) { [weak self] app, _ in | |
| self?.didChange() | |
| } | |
| } | |
| private func didChange() { | |
| icon = NSImage.appIcon | |
| } | |
| deinit { | |
| if let iconAppearanceObservation { | |
| NSWorkspace.shared.notificationCenter.removeObserver(iconAppearanceObservation) | |
| } | |
| } | |
| } | |
| private var image: NSImage { | |
| let icon = provider.icon | |
| icon.size = NSSize(width: size, height: size) | |
| return icon | |
| } | |
| public var body: some View { | |
| Image(nsImage: image) | |
| .resizable() | |
| #if canImport(WidgetKit) | |
| /// Ensure icon renders correctly in tinted widgets. | |
| .widgetAccentedRenderingMode(.fullColor) | |
| #endif | |
| .frame(width: size, height: size) | |
| } | |
| } | |
| extension NSImage { | |
| /// The main bundle app icon in the current system style. | |
| static var appIcon: NSImage { NSWorkspace.shared.icon(forFile: Bundle.main.bundlePath) } | |
| } | |
| private extension Notification.Name { | |
| static let iconAppearanceConfigurationDidChange = Notification.Name("NSWorkspaceIconAppearanceConfigurationDidChangeNotification") | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment