├── LICENSE
├── README.md
├── Sherlock.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcuserdata
│ └── mario.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── Sherlock
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ └── Main.storyboard
├── Info.plist
├── Sherlock.entitlements
└── ViewController.swift
├── SherlockDockTilePlugIn
├── Assets.xcassets
│ ├── Contents.json
│ ├── DockTile-Dark.imageset
│ │ ├── AppDark128.png
│ │ ├── AppDark128@2x.png
│ │ └── Contents.json
│ └── DockTile.imageset
│ │ ├── App128.png
│ │ ├── App128@2x.png
│ │ └── Contents.json
├── SherlockDockTilePlugIn-Bridging-Header.h
└── SherlockDockTilePlugIn.swift
└── images
├── demo.gif
├── fig1.png
├── fig10.png
├── fig11.png
├── fig12.png
├── fig2.png
├── fig3.png
├── fig4.png
├── fig5.png
├── fig6.png
├── fig7.png
├── fig8.png
└── fig9.png
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Mario Guzman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sherlock - NSDockTilePlugIn
2 |
3 | ## What is NSDockTilePlugIn?
4 |
5 | A set of methods implemented by plug-ins that allow an app’s Dock tile to be customized while the app is not running.
6 |
7 | Customizing an application’s Dock tile when the application itself is not running requires that you write a plug-in. The plug-in’s principal class must implement the NSDockTilePlugIn protocol.
8 |
9 | The name of the plugin is indicated by a NSDockTilePlugIn key in the application's Info.plist file.
10 |
11 | The plugin is loaded in a system process at login time or when the application tile is added to the Dock. When the plugin is loaded, the principal class' implementation of `setDockTile(_:)` is invoked, passing an `NSDockTile` for the plug-in to customize. If the principal class implements `dockMenu()` it is invoked whenever the user causes the application's dock menu to be shown. When the dock tile is no longer valid (for example,. the application has been removed from the dock) `setDockTile(_:)` invoked with `nil`.
12 |
13 | ## Where have I seen this be used before?
14 |
15 | `NSDockTilePlugIn` seems to have been created for Mac OS X 10.5 Leopard for iCal (now Calendar). Since Leopard, the Calendar icon displays the current date when shown in the Dock and in Launchpad. Its purpose is to run code (logic) to update its presentation showing the current date even when Calendar is not running.
16 |
17 | It is rare to see apps use `NSDockTilePlugIn` because apps that contain one are not allowed on the Mac App Store.
18 |
19 | # Adding `NSDockTilePlugIn` to your project
20 |
21 | 1. Add a new Target to your Mac application Xcode project.
22 |
23 |
24 |
25 | 2. In the Templates sheet, click the **macOS** tab and then select **Bundle** in the **Framework & Library** section. Click **Next**.
26 |
27 |
28 |
29 | 3. For convenience, name your new Target as [YourAppName]DockTilePlugIn and set the Bundle Extension to `docktileplugin`. In our example, the final name for our new Target will be “SherlockDockTilePlugIn”. Click **Finish**.
30 |
31 |
32 |
33 | 4. Xcode will not create a folder structure for this new Target. Manually create one to keep your project organized. For convenience, we are keeping the same name we’ve been using: “SherlockDockTilePlugIn”.
34 |
35 |
36 |
37 | 5. Select the “SherlockDockTilePlugIn” folder and add a new Swift file. Name the Swift file also “SherlockDockTilePlugIn”. Be sure that the correct Target is selected `SherlockDockTilePlugIn`.
38 |
39 |
40 |
41 | 6. Select the Host app from the Targets list and click the **Info** tab. Add a new Key to your Info.plist: “Dock Tile plugin path”. Set its String value to the name we chose earlier of **[YourAppName]DockTilePlugIn** and append **.docktileplugin**. In our example, this would be “SherlockDockTilePlugIn.docktileplugin”.
42 |
43 |
44 |
45 | 7. With the Host app still selected, click on the **Build Phases** tab. Click the **+** and choose “New Copy Files Phase”. This is to copy our NSDockTilePlugIn bundle into the Host app’s bundle. For Destination, select “PlugIns and Foundation Extensions” and add the plug-in to the list using the + symbol below.
46 |
47 |
48 |
49 |
50 | 8. Select the **DockTilePlugIn** again from the Targets list and click the **Info** tab. You will see “Principal” class key already added for you but with an empty value. Add just the name of your class/file created in step 5 *without* the extension. In our case here, it will just be “SherlockDockTilePlugIn”.
51 |
52 |
53 |
54 | 9. Open the Swift file and create your class “SherlockDockTilePlugIn”. This class must inherit from NSObject and must conform to NSDockTilePlugIn. Note: NSDockTilePlugIn requires AppKit, so make sure you add “import AppKit” to your import declarations.
55 |
56 |
57 |
58 | That’s all! Now we add our custom logic to have the icon do what we want it to do when the application is not running.
59 |
60 | # What is this NSDockTilePlugIn sample going to do?
61 |
62 | For this example, I will illustrate with code how to make it so that the app icon can change between light and dark mode when the system changes appearance and it will change when the Host app is not running.
63 |
64 | 
65 |
66 | ## Initial Setup
67 |
68 | Before starting, add an `Assets.xcassets` catalog to your `NSDockTilePlugIn` Target. Add both a Light and Dark mode variant of your app icon with 128x128 and 256x256 sizes for each mode. For convenience, name them “DockTile” and “DockTile-Dark” since that is what they’re named in this code (or update the code to match your preferred asset names).
69 |
70 | Additionally, add `import Combine` into your import declarations as this sample uses Combine Publishers.
71 |
72 |
73 |
74 | ## Conforming to NSDockTilePlugIn
75 |
76 | `NSDockTilePlugIn` protocol requires you conform to at least `setDockTile(_:)` which is what changes the appearance of your app’s Dock Tile (icon) when in the Dock.
77 |
78 | As mentioned above in the introduction to this article, when the user drags the Host app into the Dock, this function is called with the `dockTile` parameter having a value. When the user drags the Host app’s icon out of the Dock, this function is called again with `dockTile` being `nil`.
79 |
80 | This means, when we conform to `setDockTile(_:)`, we will have an `if-let, else` which will both update the visual appearance of the Dock icon immediately and set up a publisher to get subsequent appearance changes and will tear these publishers down in the `else` case.
81 |
82 | The `appearancePublisher` will get updates from macOS when the user changes between light and dark mode appearances. We call the same `updateTile(tile: appearance:)` functions.
83 |
84 | Please refer to the Xcode project for the entire code.
85 |
86 | ```swift
87 | func setDockTile(_ dockTile: NSDockTile?) {
88 | if let dockTile = dockTile {
89 |
90 | // A DockTile was provided by the system. Perform setup to listen
91 | // for appearance changes and system launch/termination events.
92 |
93 | // Start with an initial update to match the system immediately.
94 | updateTile(tile: dockTile)
95 |
96 | // Add a publisher for the appearance. Will get called whenever
97 | // the system appearance changes.
98 | appearancePublisher = NSApp.publisher(for: \.effectiveAppearance)
99 | .removeDuplicates()
100 | .sink(receiveValue: { appearance in
101 | self.updateTile(tile: dockTile, appearance: appearance)
102 | })
103 | } else {
104 |
105 | // Application icon was removed from the Dock. We don't need to do
106 | // unnecessary listening and event handling since the icon is not
107 | // showing in the Dock.
108 |
109 | appearancePublisher?.cancel()
110 | appearancePublisher = nil
111 | }
112 | }
113 | ```
114 |
115 |
116 |
117 | The `updateTile(tile: appearance:)` gets the appearance value passed in by the `appearancePublisher` and decides which image (for the app icon) to use.
118 |
119 | *NOTE: Please note that the way we grab an image is slightly different as we use the `NSDockTilePlugIn` class’ Bundle. See full code for details.*
120 |
121 | We also pass in a parameter for the `NSDockTile`. We create a new instance of `NSImageView` with the image we want to set and set that as the `tile`’s `contentView` property. Finally, call its `display()` function to update the icon in the Dock.
122 |
123 | ```swift
124 | private func updateTile(tile: NSDockTile, appearance: NSAppearance = NSApp.effectiveAppearance) {
125 | let isLightMode = appearance.bestMatch(from: [.aqua, .darkAqua]) == .aqua
126 | let iconName: DockTileImage = isLightMode ? .light : .dark
127 |
128 | guard let image = self.dockTilePlugInBundle.image(forResource: iconName.rawValue)
129 | else { return }
130 |
131 | let imageView = NSImageView(image: image)
132 | tile.contentView = imageView
133 | tile.display()
134 | }
135 | ```
136 |
137 | # Testing & Debugging
138 |
139 | ## Testing
140 |
141 | Because the code you’re writing is meant to run when your application is **not** running, to test you must Build & Run your app from Xcode and then persist the Host app’s icon in the Dock by Control-Clicking on the icon, selection **Options → Keep in Dock**. Otherwise, when you quit the app, the its Dock icon will also disappear.
142 |
143 | Change your app’s Appearance in Control Center or in System Settings. You will notice that the icon in the Dock will change accordingly.
144 |
145 | 
146 |
147 | ## Debugging
148 |
149 | I have found all suggested ways to debug this code to not work. The only way I have been successful was by using `NSLog` print statements and using Console.app to see code paths and crash errors. I have prefixed my `NSLog` strings with “~ ~” so that I can quickly filter the very noisy Console.app output.
150 |
151 | # What about when the app *is* running?
152 |
153 | You can also alter the icon and add additional menu items to it when the app is running. However, that is more common and *is* allowed for apps published on Mac App Store. For more information on these implementations, visit:
154 |
155 | - [applicationDockMenu(_:)](https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428564-applicationdockmenu)
156 | - [applicationIconImage](https://developer.apple.com/documentation/appkit/nsapplication/1428744-applicationiconimage)
157 |
158 | # Find Me Online
159 |
160 | - [My Website/Portfolio](https://marioaguzman.github.io/)
161 | - [Mastodon (mastodon.social)](https://mastodon.social/@marioguzman)
162 | - [My Music apps for Apple Music on macOS](https://marioaguzman.github.io/music) *This tutorial brought to you by my Music apps — which is where I learned how to implement `NSDockTilePlugIn`.*
163 |
--------------------------------------------------------------------------------
/Sherlock.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 77;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | ACA427062CAF9FDE003309E1 /* SherlockDockTilePlugIn.docktileplugin in CopyFiles */ = {isa = PBXBuildFile; fileRef = ACA426F72CAF9DBA003309E1 /* SherlockDockTilePlugIn.docktileplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
11 | /* End PBXBuildFile section */
12 |
13 | /* Begin PBXCopyFilesBuildPhase section */
14 | ACA427052CAF9FCC003309E1 /* CopyFiles */ = {
15 | isa = PBXCopyFilesBuildPhase;
16 | buildActionMask = 2147483647;
17 | dstPath = "";
18 | dstSubfolderSpec = 13;
19 | files = (
20 | ACA427062CAF9FDE003309E1 /* SherlockDockTilePlugIn.docktileplugin in CopyFiles */,
21 | );
22 | runOnlyForDeploymentPostprocessing = 0;
23 | };
24 | /* End PBXCopyFilesBuildPhase section */
25 |
26 | /* Begin PBXFileReference section */
27 | ACA426E12CAF9D53003309E1 /* Sherlock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sherlock.app; sourceTree = BUILT_PRODUCTS_DIR; };
28 | ACA426F72CAF9DBA003309E1 /* SherlockDockTilePlugIn.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SherlockDockTilePlugIn.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
29 | /* End PBXFileReference section */
30 |
31 | /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
32 | ACA427022CAF9F2C003309E1 /* Exceptions for "SherlockDockTilePlugIn" folder in "SherlockDockTilePlugIn" target */ = {
33 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
34 | membershipExceptions = (
35 | Assets.xcassets,
36 | SherlockDockTilePlugIn.swift,
37 | );
38 | target = ACA426F62CAF9DBA003309E1 /* SherlockDockTilePlugIn */;
39 | };
40 | ACA427042CAF9F4D003309E1 /* Exceptions for "Sherlock" folder in "Sherlock" target */ = {
41 | isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
42 | membershipExceptions = (
43 | Info.plist,
44 | );
45 | target = ACA426E02CAF9D53003309E1 /* Sherlock */;
46 | };
47 | /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
48 |
49 | /* Begin PBXFileSystemSynchronizedRootGroup section */
50 | ACA426E32CAF9D53003309E1 /* Sherlock */ = {
51 | isa = PBXFileSystemSynchronizedRootGroup;
52 | exceptions = (
53 | ACA427042CAF9F4D003309E1 /* Exceptions for "Sherlock" folder in "Sherlock" target */,
54 | );
55 | path = Sherlock;
56 | sourceTree = "";
57 | };
58 | ACA426FB2CAF9DD1003309E1 /* SherlockDockTilePlugIn */ = {
59 | isa = PBXFileSystemSynchronizedRootGroup;
60 | exceptions = (
61 | ACA427022CAF9F2C003309E1 /* Exceptions for "SherlockDockTilePlugIn" folder in "SherlockDockTilePlugIn" target */,
62 | );
63 | path = SherlockDockTilePlugIn;
64 | sourceTree = "";
65 | };
66 | /* End PBXFileSystemSynchronizedRootGroup section */
67 |
68 | /* Begin PBXFrameworksBuildPhase section */
69 | ACA426DE2CAF9D53003309E1 /* Frameworks */ = {
70 | isa = PBXFrameworksBuildPhase;
71 | buildActionMask = 2147483647;
72 | files = (
73 | );
74 | runOnlyForDeploymentPostprocessing = 0;
75 | };
76 | ACA426F42CAF9DBA003309E1 /* Frameworks */ = {
77 | isa = PBXFrameworksBuildPhase;
78 | buildActionMask = 2147483647;
79 | files = (
80 | );
81 | runOnlyForDeploymentPostprocessing = 0;
82 | };
83 | /* End PBXFrameworksBuildPhase section */
84 |
85 | /* Begin PBXGroup section */
86 | ACA426D82CAF9D53003309E1 = {
87 | isa = PBXGroup;
88 | children = (
89 | ACA426E32CAF9D53003309E1 /* Sherlock */,
90 | ACA426E22CAF9D53003309E1 /* Products */,
91 | ACA426FB2CAF9DD1003309E1 /* SherlockDockTilePlugIn */,
92 | );
93 | sourceTree = "";
94 | };
95 | ACA426E22CAF9D53003309E1 /* Products */ = {
96 | isa = PBXGroup;
97 | children = (
98 | ACA426E12CAF9D53003309E1 /* Sherlock.app */,
99 | ACA426F72CAF9DBA003309E1 /* SherlockDockTilePlugIn.docktileplugin */,
100 | );
101 | name = Products;
102 | sourceTree = "";
103 | };
104 | /* End PBXGroup section */
105 |
106 | /* Begin PBXNativeTarget section */
107 | ACA426E02CAF9D53003309E1 /* Sherlock */ = {
108 | isa = PBXNativeTarget;
109 | buildConfigurationList = ACA426F02CAF9D54003309E1 /* Build configuration list for PBXNativeTarget "Sherlock" */;
110 | buildPhases = (
111 | ACA426DD2CAF9D53003309E1 /* Sources */,
112 | ACA426DE2CAF9D53003309E1 /* Frameworks */,
113 | ACA426DF2CAF9D53003309E1 /* Resources */,
114 | ACA427052CAF9FCC003309E1 /* CopyFiles */,
115 | );
116 | buildRules = (
117 | );
118 | dependencies = (
119 | );
120 | fileSystemSynchronizedGroups = (
121 | ACA426E32CAF9D53003309E1 /* Sherlock */,
122 | ACA426FB2CAF9DD1003309E1 /* SherlockDockTilePlugIn */,
123 | );
124 | name = Sherlock;
125 | packageProductDependencies = (
126 | );
127 | productName = Sherlock;
128 | productReference = ACA426E12CAF9D53003309E1 /* Sherlock.app */;
129 | productType = "com.apple.product-type.application";
130 | };
131 | ACA426F62CAF9DBA003309E1 /* SherlockDockTilePlugIn */ = {
132 | isa = PBXNativeTarget;
133 | buildConfigurationList = ACA426F82CAF9DBA003309E1 /* Build configuration list for PBXNativeTarget "SherlockDockTilePlugIn" */;
134 | buildPhases = (
135 | ACA426F32CAF9DBA003309E1 /* Sources */,
136 | ACA426F42CAF9DBA003309E1 /* Frameworks */,
137 | ACA426F52CAF9DBA003309E1 /* Resources */,
138 | );
139 | buildRules = (
140 | );
141 | dependencies = (
142 | );
143 | name = SherlockDockTilePlugIn;
144 | packageProductDependencies = (
145 | );
146 | productName = SherlockDockTilePlugIn;
147 | productReference = ACA426F72CAF9DBA003309E1 /* SherlockDockTilePlugIn.docktileplugin */;
148 | productType = "com.apple.product-type.bundle";
149 | };
150 | /* End PBXNativeTarget section */
151 |
152 | /* Begin PBXProject section */
153 | ACA426D92CAF9D53003309E1 /* Project object */ = {
154 | isa = PBXProject;
155 | attributes = {
156 | BuildIndependentTargetsInParallel = 1;
157 | LastSwiftUpdateCheck = 1600;
158 | LastUpgradeCheck = 1600;
159 | TargetAttributes = {
160 | ACA426E02CAF9D53003309E1 = {
161 | CreatedOnToolsVersion = 16.0;
162 | };
163 | ACA426F62CAF9DBA003309E1 = {
164 | CreatedOnToolsVersion = 16.0;
165 | LastSwiftMigration = 1600;
166 | };
167 | };
168 | };
169 | buildConfigurationList = ACA426DC2CAF9D53003309E1 /* Build configuration list for PBXProject "Sherlock" */;
170 | developmentRegion = en;
171 | hasScannedForEncodings = 0;
172 | knownRegions = (
173 | en,
174 | Base,
175 | );
176 | mainGroup = ACA426D82CAF9D53003309E1;
177 | minimizedProjectReferenceProxies = 1;
178 | preferredProjectObjectVersion = 77;
179 | productRefGroup = ACA426E22CAF9D53003309E1 /* Products */;
180 | projectDirPath = "";
181 | projectRoot = "";
182 | targets = (
183 | ACA426E02CAF9D53003309E1 /* Sherlock */,
184 | ACA426F62CAF9DBA003309E1 /* SherlockDockTilePlugIn */,
185 | );
186 | };
187 | /* End PBXProject section */
188 |
189 | /* Begin PBXResourcesBuildPhase section */
190 | ACA426DF2CAF9D53003309E1 /* Resources */ = {
191 | isa = PBXResourcesBuildPhase;
192 | buildActionMask = 2147483647;
193 | files = (
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | ACA426F52CAF9DBA003309E1 /* Resources */ = {
198 | isa = PBXResourcesBuildPhase;
199 | buildActionMask = 2147483647;
200 | files = (
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | };
204 | /* End PBXResourcesBuildPhase section */
205 |
206 | /* Begin PBXSourcesBuildPhase section */
207 | ACA426DD2CAF9D53003309E1 /* Sources */ = {
208 | isa = PBXSourcesBuildPhase;
209 | buildActionMask = 2147483647;
210 | files = (
211 | );
212 | runOnlyForDeploymentPostprocessing = 0;
213 | };
214 | ACA426F32CAF9DBA003309E1 /* Sources */ = {
215 | isa = PBXSourcesBuildPhase;
216 | buildActionMask = 2147483647;
217 | files = (
218 | );
219 | runOnlyForDeploymentPostprocessing = 0;
220 | };
221 | /* End PBXSourcesBuildPhase section */
222 |
223 | /* Begin XCBuildConfiguration section */
224 | ACA426EE2CAF9D54003309E1 /* Debug */ = {
225 | isa = XCBuildConfiguration;
226 | buildSettings = {
227 | ALWAYS_SEARCH_USER_PATHS = NO;
228 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
229 | CLANG_ANALYZER_NONNULL = YES;
230 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
231 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
232 | CLANG_ENABLE_MODULES = YES;
233 | CLANG_ENABLE_OBJC_ARC = YES;
234 | CLANG_ENABLE_OBJC_WEAK = YES;
235 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
236 | CLANG_WARN_BOOL_CONVERSION = YES;
237 | CLANG_WARN_COMMA = YES;
238 | CLANG_WARN_CONSTANT_CONVERSION = YES;
239 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
241 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
242 | CLANG_WARN_EMPTY_BODY = YES;
243 | CLANG_WARN_ENUM_CONVERSION = YES;
244 | CLANG_WARN_INFINITE_RECURSION = YES;
245 | CLANG_WARN_INT_CONVERSION = YES;
246 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
247 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
248 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
249 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
250 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
251 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
252 | CLANG_WARN_STRICT_PROTOTYPES = YES;
253 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
254 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
255 | CLANG_WARN_UNREACHABLE_CODE = YES;
256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
257 | COPY_PHASE_STRIP = NO;
258 | DEBUG_INFORMATION_FORMAT = dwarf;
259 | ENABLE_STRICT_OBJC_MSGSEND = YES;
260 | ENABLE_TESTABILITY = YES;
261 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
262 | GCC_C_LANGUAGE_STANDARD = gnu17;
263 | GCC_DYNAMIC_NO_PIC = NO;
264 | GCC_NO_COMMON_BLOCKS = YES;
265 | GCC_OPTIMIZATION_LEVEL = 0;
266 | GCC_PREPROCESSOR_DEFINITIONS = (
267 | "DEBUG=1",
268 | "$(inherited)",
269 | );
270 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
271 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
272 | GCC_WARN_UNDECLARED_SELECTOR = YES;
273 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
274 | GCC_WARN_UNUSED_FUNCTION = YES;
275 | GCC_WARN_UNUSED_VARIABLE = YES;
276 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
277 | MACOSX_DEPLOYMENT_TARGET = 15.0;
278 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
279 | MTL_FAST_MATH = YES;
280 | ONLY_ACTIVE_ARCH = YES;
281 | SDKROOT = macosx;
282 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
283 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
284 | };
285 | name = Debug;
286 | };
287 | ACA426EF2CAF9D54003309E1 /* Release */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ALWAYS_SEARCH_USER_PATHS = NO;
291 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
292 | CLANG_ANALYZER_NONNULL = YES;
293 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
294 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
295 | CLANG_ENABLE_MODULES = YES;
296 | CLANG_ENABLE_OBJC_ARC = YES;
297 | CLANG_ENABLE_OBJC_WEAK = YES;
298 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
299 | CLANG_WARN_BOOL_CONVERSION = YES;
300 | CLANG_WARN_COMMA = YES;
301 | CLANG_WARN_CONSTANT_CONVERSION = YES;
302 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
304 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
305 | CLANG_WARN_EMPTY_BODY = YES;
306 | CLANG_WARN_ENUM_CONVERSION = YES;
307 | CLANG_WARN_INFINITE_RECURSION = YES;
308 | CLANG_WARN_INT_CONVERSION = YES;
309 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
310 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
313 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
314 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
315 | CLANG_WARN_STRICT_PROTOTYPES = YES;
316 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
317 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
318 | CLANG_WARN_UNREACHABLE_CODE = YES;
319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
320 | COPY_PHASE_STRIP = NO;
321 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
322 | ENABLE_NS_ASSERTIONS = NO;
323 | ENABLE_STRICT_OBJC_MSGSEND = YES;
324 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
325 | GCC_C_LANGUAGE_STANDARD = gnu17;
326 | GCC_NO_COMMON_BLOCKS = YES;
327 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
328 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
329 | GCC_WARN_UNDECLARED_SELECTOR = YES;
330 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
331 | GCC_WARN_UNUSED_FUNCTION = YES;
332 | GCC_WARN_UNUSED_VARIABLE = YES;
333 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
334 | MACOSX_DEPLOYMENT_TARGET = 15.0;
335 | MTL_ENABLE_DEBUG_INFO = NO;
336 | MTL_FAST_MATH = YES;
337 | SDKROOT = macosx;
338 | SWIFT_COMPILATION_MODE = wholemodule;
339 | };
340 | name = Release;
341 | };
342 | ACA426F12CAF9D54003309E1 /* Debug */ = {
343 | isa = XCBuildConfiguration;
344 | buildSettings = {
345 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
346 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
347 | CODE_SIGN_ENTITLEMENTS = Sherlock/Sherlock.entitlements;
348 | CODE_SIGN_STYLE = Automatic;
349 | COMBINE_HIDPI_IMAGES = YES;
350 | CURRENT_PROJECT_VERSION = 1;
351 | DEVELOPMENT_TEAM = 4N3K4K49FN;
352 | ENABLE_HARDENED_RUNTIME = YES;
353 | GENERATE_INFOPLIST_FILE = YES;
354 | INFOPLIST_FILE = Sherlock/Info.plist;
355 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
356 | INFOPLIST_KEY_NSMainStoryboardFile = Main;
357 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
358 | LD_RUNPATH_SEARCH_PATHS = (
359 | "$(inherited)",
360 | "@executable_path/../Frameworks",
361 | );
362 | MARKETING_VERSION = 1.0;
363 | PRODUCT_BUNDLE_IDENTIFIER = com.marioaguzman.Sherlock;
364 | PRODUCT_NAME = "$(TARGET_NAME)";
365 | SWIFT_EMIT_LOC_STRINGS = YES;
366 | SWIFT_VERSION = 5.0;
367 | };
368 | name = Debug;
369 | };
370 | ACA426F22CAF9D54003309E1 /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
374 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
375 | CODE_SIGN_ENTITLEMENTS = Sherlock/Sherlock.entitlements;
376 | CODE_SIGN_STYLE = Automatic;
377 | COMBINE_HIDPI_IMAGES = YES;
378 | CURRENT_PROJECT_VERSION = 1;
379 | DEVELOPMENT_TEAM = 4N3K4K49FN;
380 | ENABLE_HARDENED_RUNTIME = YES;
381 | GENERATE_INFOPLIST_FILE = YES;
382 | INFOPLIST_FILE = Sherlock/Info.plist;
383 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
384 | INFOPLIST_KEY_NSMainStoryboardFile = Main;
385 | INFOPLIST_KEY_NSPrincipalClass = NSApplication;
386 | LD_RUNPATH_SEARCH_PATHS = (
387 | "$(inherited)",
388 | "@executable_path/../Frameworks",
389 | );
390 | MARKETING_VERSION = 1.0;
391 | PRODUCT_BUNDLE_IDENTIFIER = com.marioaguzman.Sherlock;
392 | PRODUCT_NAME = "$(TARGET_NAME)";
393 | SWIFT_EMIT_LOC_STRINGS = YES;
394 | SWIFT_VERSION = 5.0;
395 | };
396 | name = Release;
397 | };
398 | ACA426F92CAF9DBA003309E1 /* Debug */ = {
399 | isa = XCBuildConfiguration;
400 | buildSettings = {
401 | CLANG_ENABLE_MODULES = YES;
402 | CODE_SIGN_STYLE = Automatic;
403 | COMBINE_HIDPI_IMAGES = YES;
404 | CURRENT_PROJECT_VERSION = 1;
405 | DEVELOPMENT_TEAM = 4N3K4K49FN;
406 | GENERATE_INFOPLIST_FILE = YES;
407 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
408 | INFOPLIST_KEY_NSPrincipalClass = SherlockDockTilePlugIn;
409 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
410 | MARKETING_VERSION = 1.0;
411 | PRODUCT_BUNDLE_IDENTIFIER = com.marioaguzman.SherlockDockTilePlugIn;
412 | PRODUCT_NAME = "$(TARGET_NAME)";
413 | SKIP_INSTALL = YES;
414 | SWIFT_EMIT_LOC_STRINGS = YES;
415 | SWIFT_OBJC_BRIDGING_HEADER = "SherlockDockTilePlugIn/SherlockDockTilePlugIn-Bridging-Header.h";
416 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
417 | SWIFT_VERSION = 5.0;
418 | WRAPPER_EXTENSION = docktileplugin;
419 | };
420 | name = Debug;
421 | };
422 | ACA426FA2CAF9DBA003309E1 /* Release */ = {
423 | isa = XCBuildConfiguration;
424 | buildSettings = {
425 | CLANG_ENABLE_MODULES = YES;
426 | CODE_SIGN_STYLE = Automatic;
427 | COMBINE_HIDPI_IMAGES = YES;
428 | CURRENT_PROJECT_VERSION = 1;
429 | DEVELOPMENT_TEAM = 4N3K4K49FN;
430 | GENERATE_INFOPLIST_FILE = YES;
431 | INFOPLIST_KEY_NSHumanReadableCopyright = "";
432 | INFOPLIST_KEY_NSPrincipalClass = SherlockDockTilePlugIn;
433 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
434 | MARKETING_VERSION = 1.0;
435 | PRODUCT_BUNDLE_IDENTIFIER = com.marioaguzman.SherlockDockTilePlugIn;
436 | PRODUCT_NAME = "$(TARGET_NAME)";
437 | SKIP_INSTALL = YES;
438 | SWIFT_EMIT_LOC_STRINGS = YES;
439 | SWIFT_OBJC_BRIDGING_HEADER = "SherlockDockTilePlugIn/SherlockDockTilePlugIn-Bridging-Header.h";
440 | SWIFT_VERSION = 5.0;
441 | WRAPPER_EXTENSION = docktileplugin;
442 | };
443 | name = Release;
444 | };
445 | /* End XCBuildConfiguration section */
446 |
447 | /* Begin XCConfigurationList section */
448 | ACA426DC2CAF9D53003309E1 /* Build configuration list for PBXProject "Sherlock" */ = {
449 | isa = XCConfigurationList;
450 | buildConfigurations = (
451 | ACA426EE2CAF9D54003309E1 /* Debug */,
452 | ACA426EF2CAF9D54003309E1 /* Release */,
453 | );
454 | defaultConfigurationIsVisible = 0;
455 | defaultConfigurationName = Release;
456 | };
457 | ACA426F02CAF9D54003309E1 /* Build configuration list for PBXNativeTarget "Sherlock" */ = {
458 | isa = XCConfigurationList;
459 | buildConfigurations = (
460 | ACA426F12CAF9D54003309E1 /* Debug */,
461 | ACA426F22CAF9D54003309E1 /* Release */,
462 | );
463 | defaultConfigurationIsVisible = 0;
464 | defaultConfigurationName = Release;
465 | };
466 | ACA426F82CAF9DBA003309E1 /* Build configuration list for PBXNativeTarget "SherlockDockTilePlugIn" */ = {
467 | isa = XCConfigurationList;
468 | buildConfigurations = (
469 | ACA426F92CAF9DBA003309E1 /* Debug */,
470 | ACA426FA2CAF9DBA003309E1 /* Release */,
471 | );
472 | defaultConfigurationIsVisible = 0;
473 | defaultConfigurationName = Release;
474 | };
475 | /* End XCConfigurationList section */
476 | };
477 | rootObject = ACA426D92CAF9D53003309E1 /* Project object */;
478 | }
479 |
--------------------------------------------------------------------------------
/Sherlock.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Sherlock.xcodeproj/xcuserdata/mario.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Sherlock.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 | SherlockDockTilePlugIn.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Sherlock/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Sherlock
4 | //
5 | // Created by Mario Guzman on 10/3/24.
6 | //
7 |
8 | import Cocoa
9 |
10 | @main
11 | class AppDelegate: NSObject, NSApplicationDelegate {
12 |
13 |
14 |
15 |
16 | func applicationDidFinishLaunching(_ aNotification: Notification) {
17 | // Insert code here to initialize your application
18 | }
19 |
20 | func applicationWillTerminate(_ aNotification: Notification) {
21 | // Insert code here to tear down your application
22 | }
23 |
24 | func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
25 | return true
26 | }
27 |
28 |
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/Sherlock/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Sherlock/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "scale" : "2x",
31 | "size" : "128x128"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "scale" : "1x",
36 | "size" : "256x256"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "scale" : "2x",
41 | "size" : "256x256"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "scale" : "1x",
46 | "size" : "512x512"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "scale" : "2x",
51 | "size" : "512x512"
52 | }
53 | ],
54 | "info" : {
55 | "author" : "xcode",
56 | "version" : 1
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Sherlock/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sherlock/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
--------------------------------------------------------------------------------
/Sherlock/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSDockTilePlugIn
6 | SherlockDockTilePlugIn.docktileplugin
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sherlock/Sherlock.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Sherlock/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Sherlock
4 | //
5 | // Created by Mario Guzman on 10/3/24.
6 | //
7 |
8 | import Cocoa
9 |
10 | class ViewController: NSViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 |
15 | // Do any additional setup after loading the view.
16 | }
17 |
18 | override var representedObject: Any? {
19 | didSet {
20 | // Update the view, if already loaded.
21 | }
22 | }
23 |
24 |
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile-Dark.imageset/AppDark128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/SherlockDockTilePlugIn/Assets.xcassets/DockTile-Dark.imageset/AppDark128.png
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile-Dark.imageset/AppDark128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/SherlockDockTilePlugIn/Assets.xcassets/DockTile-Dark.imageset/AppDark128@2x.png
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile-Dark.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "AppDark128.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "AppDark128@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile.imageset/App128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/SherlockDockTilePlugIn/Assets.xcassets/DockTile.imageset/App128.png
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile.imageset/App128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/SherlockDockTilePlugIn/Assets.xcassets/DockTile.imageset/App128@2x.png
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/Assets.xcassets/DockTile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "App128.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "App128@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/SherlockDockTilePlugIn-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 |
--------------------------------------------------------------------------------
/SherlockDockTilePlugIn/SherlockDockTilePlugIn.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SherlockDockTilePlugIn.swift
3 | // SherlockDockTilePlugIn
4 | //
5 | // Created by Mario Guzman on 10/3/24.
6 | //
7 |
8 | import AppKit
9 | import Combine
10 |
11 | class SherlockDockTilePlugIn: NSObject, NSDockTilePlugIn {
12 |
13 | private var appearancePublisher: AnyCancellable? = nil
14 | private enum DockTileImage: String {
15 | case light = "DockTile"
16 | case dark = "DockTile-Dark"
17 | }
18 |
19 | /// The associated Dock icon images will be provided in this bundle, not the host application's
20 | /// bundle. This is a convenience property to get the appropriate icon when it needs to change.
21 | private var dockTilePlugInBundle: Bundle = {
22 | Bundle(for: SherlockDockTilePlugIn.self)
23 | }()
24 |
25 | /// When you drag this app into the Dock, it will call this function, `setDockTile(_ :)` with
26 | /// a non-nil value for its `dockTile` parameter. When you drag out this app's icon out of the
27 | /// Dock, it will call this again with the `dockTile` parameter set to `nil`.
28 | /// Perform any setup when `dockTile` has a value and any cleanup when it is `nil`.
29 | func setDockTile(_ dockTile: NSDockTile?) {
30 |
31 | if let dockTile = dockTile {
32 |
33 | // A DockTile was provided by the system. Perform setup to listen
34 | // for appearance changes and system launch/termination events.
35 |
36 | // Start with an initial update to match the system immediately.
37 | updateTile(tile: dockTile)
38 |
39 | // Add a publisher for the appearance. Will get called whenever
40 | // the system appearance changes.
41 | appearancePublisher = NSApp.publisher(for: \.effectiveAppearance)
42 | .removeDuplicates()
43 | .sink(receiveValue: { appearance in
44 | self.updateTile(tile: dockTile, appearance: appearance)
45 | })
46 | } else {
47 |
48 | // Application icon was removed from the Dock. We don't need to do
49 | // unnecessary listening and event handling since the icon is not
50 | // showing in the Dock.
51 |
52 | appearancePublisher?.cancel()
53 | appearancePublisher = nil
54 | }
55 | }
56 |
57 | /// Implement this function if you'd like to provide additional menu items for when the host
58 | /// application is not running. These will appear in addition to the system-provided options when
59 | /// the user control-clicks on the Host app's Dock icon.
60 | func dockMenu() -> NSMenu? { return nil }
61 |
62 | // MARK: - Helper Functions
63 |
64 | private func updateTile(tile: NSDockTile, appearance: NSAppearance = NSApp.effectiveAppearance) {
65 | let isLightMode = appearance.bestMatch(from: [.aqua, .darkAqua]) == .aqua
66 | let iconName: DockTileImage = isLightMode ? .light : .dark
67 |
68 | guard let image = self.dockTilePlugInBundle.image(forResource: iconName.rawValue)
69 | else { return }
70 |
71 | let imageView = NSImageView(image: image)
72 | tile.contentView = imageView
73 | tile.display()
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/images/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/demo.gif
--------------------------------------------------------------------------------
/images/fig1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig1.png
--------------------------------------------------------------------------------
/images/fig10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig10.png
--------------------------------------------------------------------------------
/images/fig11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig11.png
--------------------------------------------------------------------------------
/images/fig12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig12.png
--------------------------------------------------------------------------------
/images/fig2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig2.png
--------------------------------------------------------------------------------
/images/fig3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig3.png
--------------------------------------------------------------------------------
/images/fig4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig4.png
--------------------------------------------------------------------------------
/images/fig5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig5.png
--------------------------------------------------------------------------------
/images/fig6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig6.png
--------------------------------------------------------------------------------
/images/fig7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig7.png
--------------------------------------------------------------------------------
/images/fig8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig8.png
--------------------------------------------------------------------------------
/images/fig9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/marioaguzman/NSDockTilePlugIn-Example/bb212cd134fd65b03f7275b11e1285b369b39628/images/fig9.png
--------------------------------------------------------------------------------