├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── followdown │ └── main.swift └── demo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | Package.resolved 4 | .swiftpm 5 | /*.xcodeproj 6 | xcuserdata/ 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Guilherme Rambo 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | - Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | - Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "followdown", 7 | products: [ 8 | .executable(name: "followdown", targets: ["followdown"]) 9 | ], 10 | dependencies: [ 11 | .package(url: "https://github.com/JohnSundell/ShellOut.git", from: "2.0.0") 12 | ], 13 | targets: [ 14 | .target( 15 | name: "followdown", 16 | dependencies: ["ShellOut"] 17 | ) 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FollowDown 2 | 3 | Clean up annoying badges in System Preferences. 4 | 5 | **WARNING: This tool was made in a hurry to fix an issue on my Mac. It messes with the Dock preferences property list, so I strongly recommend making a backup before running it, just in case. I'm not proud of this code, don't use it as a reference. This was only tested on macOS 10.15.4 on a system with SIP disabled, I have no idea how it behaves in other environments. Feel free to send PRs for fixing any issues you encounter.** 6 | 7 | If you're stuck with a badge in your Apple ID preference pane (or any other type of badge on macOS), this will likely be useful for you. This command-line tool will delete the CoreFollowUp database (hence its name), remove badges from System Preferences panes and the app itself in the Dock, and restart the appropriate daemons. 8 | 9 | ![demo](./demo.gif) 10 | 11 | ## Building 12 | 13 | Run `swift build`. 14 | -------------------------------------------------------------------------------- /Sources/followdown/main.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import ShellOut 3 | 4 | // Delete FollowUp DB 5 | _ = try? shellOut(to: "/bin/rm", arguments: ["~/Library/CoreFollowUp/items.db"]) 6 | 7 | // Restart FollowUp daemon 8 | _ = try? shellOut(to: "/usr/bin/killall", arguments: ["followupd"]) 9 | 10 | // Remove badge from within System Preferences 11 | _ = try? shellOut(to: "/usr/bin/defaults", arguments: ["delete", "com.apple.systempreferences", "AttentionPrefBundleIDs"]) 12 | 13 | let dockPrefsPath = NSHomeDirectory() + "/Library/Preferences/com.apple.dock.plist" 14 | 15 | if let dockPrefs = NSMutableDictionary(contentsOfFile: dockPrefsPath), var apps = dockPrefs["persistent-apps"] as? [NSDictionary] { 16 | if let prefsIdx = apps.firstIndex(where: { ($0["tile-data"] as? NSDictionary)?["file-label"] as? String == "System Preferences" }) { 17 | let prefsDict = apps[prefsIdx].mutableCopy() as! NSMutableDictionary 18 | let tileData = (prefsDict["tile-data"] as! NSDictionary).mutableCopy() as! NSMutableDictionary 19 | tileData["dock-extra"] = 0 20 | prefsDict["tile-data"] = tileData 21 | apps[prefsIdx] = prefsDict 22 | dockPrefs["persistent-apps"] = apps 23 | _ = dockPrefs.write(toFile: dockPrefsPath, atomically: true) 24 | } 25 | } 26 | 27 | // Restart Dock and cfprefsd 28 | _ = try? shellOut(to: "/usr/bin/killall", arguments: ["cfprefsd"]) 29 | _ = try? shellOut(to: "/usr/bin/killall", arguments: ["Dock"]) 30 | 31 | print("👵🏼 This house is now clean!") 32 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/insidegui/followdown/c3c517fc1c84dc9cb084e4c8e4314da75ebfb40d/demo.gif --------------------------------------------------------------------------------