1

Background When menu bar items are rearranged manually using Cmd+drag, macOS writes a new "preferred position" preference to each app that is showing a menu bar icon. (This preference helps, but does not guarantee, that they retain their position for subsequent launches.)

Problem I want to automate the saving of the menu bar icon positions, without interacting manually, for educational and accessibility reasons.

Question How can I automate the repositioning of a menu bar item (which will trigger the system to write the new prefs)?

Or, how can I trigger macOS to write the new preferences directly (without automating a repositioning of a menu bar item)?

I do not want to interact with the menu items manually, or write out the preferences myself.

8
  • 1
    I just moved some menulet icons around, the systemuiserver plist got updated immediately. Why do you want to manually trigger this update?
    – nohillside
    Jul 13, 2020 at 13:29
  • Have a look at: Bartender 3 "Bartender 3 lets you organize your menu bar icons, by hiding them, rearranging them, show hidden items with a click or keyboard shortcut and have icons show when they update." - 4 week free trial, $15 USD. - Not affiliated in any way, other then having used it in the past. Jul 13, 2020 at 20:18
  • @user3439894 I prefer Dozer which is free and open source, there are other similar apps too. I wonder how Bartender triggers this refresh. Jul 14, 2020 at 0:13
  • 1
    You'll forgive my questions, but surely they would need a more general solution for everything that has to be done with mouse and keyboard? And to what extent is the order of menulets significant? There are major accessibility features coming in Big Sur, particular voice control, if that's a help.
    – benwiggy
    Jul 14, 2020 at 11:53
  • 1
    @benwiggy I appreciate your concern. This problem is one part of a bigger picture. I think the question is quite clear and succinct, regardless of the motivations. I'm reluctant to have to justify my reasons for asking it. Hope you understand. Jul 14, 2020 at 12:10

2 Answers 2

3

I've found a way to directly write the Preferred Position values using the shell command defaults. I'll leave the automation part out since I don't know the specifics of your project (I assume you know how to automate shell commands as the current user.) This should allow you to change the preferred values but won't move the menu bar items without a restart.

Background


The Defaults system is used to store certain user preferences on macOS. This happens to include the Preferred Position for Menu Bar items. I'll let Apple explain the basics better:

From man defaults:

Defaults allows users to read, write, and delete Mac OS X user defaults from a command-line shell. Mac OS X applications and other programs use the defaults system to record user preferences and other information that must be maintained when the applications aren't running (such as default font for new documents, or the position of an Info panel). Much of this information is accessible through an application's Preferences panel, but some of it isn't, such as the position of the Info panel.

User defaults belong to domains, which typically correspond to individual applications. Each domain has a dictionary of keys and values representing its defaults; for example, "Default Font" = "Helvetica". Keys are always strings, but values can be complex data structures comprising arrays, dictionaries, strings, and binary data. These data structures are stored as XML Property Lists.

In short, Defaults are categorized under Domains (the app or service the setting belongs to), are identified using Keys (the name of the particular setting), and each *Key has a Value assigned to it.


The Preferred Position defaults we care about are not all under one domain, since individual apps can add new items to the Menu Bar. However, the keys all share this format:

NSStatusItem Preferred Position <menu-item-name>

where <menu-item-name> is the name of each item a particular app or service adds.

The values are all stored as float numbers, usually integers but some have a decimal point.

Here are the macOS built-in Menu Item Defaults on my machine as an example.

'com.apple.systemuiserver': {
    "NSStatusItem Preferred Position Item-0" = 23;
    "NSStatusItem Preferred Position Siri" = 61;
    "NSStatusItem Preferred Position com.apple.menuextra.TimeMachine" = 548;
    "NSStatusItem Preferred Position com.apple.menuextra.airport" = 483;
    "NSStatusItem Preferred Position com.apple.menuextra.battery" = 179;
    "NSStatusItem Preferred Position com.apple.menuextra.bluetooth" = 513;
    "NSStatusItem Preferred Position com.apple.menuextra.textinput" = 127;
    "NSStatusItem Preferred Position com.apple.menuextra.volume" = 260;
}

com.apple.systemuiserver is a domain containing the key NSStatusItem Preferred Position Siri with value 61.

Listing All Current Preferred Position Defaults


In order to list all the domains, keys, and values for our Preferred Position Defaults, we can use the defaults find command to search for matches.

The command we need in this instance is defaults find "NSStatusItem Preferred Position".

$ defaults find "NSStatusItem Preferred Position"
Found 1 keys in domain 'com.coconut-flavour.coconutBattery-Menu': {
    "NSStatusItem Preferred Position Item-0" = "344.5";
}
Found 1 keys in domain 'com.lastpass.LastPass': {
    "NSStatusItem Preferred Position Item-0" = 795;
}
Found 8 keys in domain 'com.apple.systemuiserver': {
    "NSStatusItem Preferred Position Item-0" = 23;
    "NSStatusItem Preferred Position Siri" = 61;
    "NSStatusItem Preferred Position com.apple.menuextra.TimeMachine" = 548;
    "NSStatusItem Preferred Position com.apple.menuextra.airport" = 483;
    "NSStatusItem Preferred Position com.apple.menuextra.battery" = 179;
    "NSStatusItem Preferred Position com.apple.menuextra.bluetooth" = 513;
    "NSStatusItem Preferred Position com.apple.menuextra.textinput" = 127;
    "NSStatusItem Preferred Position com.apple.menuextra.volume" = 260;
}
Found 1 keys in domain 'org.pqrs.Karabiner-Menu': {
    "NSStatusItem Preferred Position Item-0" = 750;
}
Found 1 keys in domain 'com.google.GoogleDrive': {
    "NSStatusItem Preferred Position Item-0" = 607;
}
... and so on ...

Reading and Writing the Value of a Given Domain & Key


In order to read the value of a given domain and key, we need to use the defaults read command. To write, we use defaults write.

Here, I will read the value of the key NSStatusItem Preferred Position Siri at the com.apple.systemuiserver domain.

Note that the domain and key must be enclosed in double quotes.

$ defaults read "com.apple.systemuiserver" "NSStatusItem Preferred Position Siri"
61

Here, I will change the the key NSStatusItem Preferred Position Siri at the com.apple.systemuiserver domain to the value 42.7.

Note that the domain and key must be enclosed in double quotes, and the -float before the value

$ defaults write "com.apple.systemuiserver" "NSStatusItem Preferred Position Siri" -float 42.7
$ defaults read "com.apple.systemuiserver" "NSStatusItem Preferred Position Siri"
42.7

Again, I'll leave exactly how you automate this up to you, but this can all be easily integrated into your code.

If this doesn't work out for you, here are some links to the Apple Developer AppKit docs for adding items to the Menu Bar as well as for accessing the Defaults system using objective-c and swift. Not sure if they'll be any use to you but maybe they'll set you on the right track.

https://developer.apple.com/documentation/appkit/nsstatusitem

https://developer.apple.com/documentation/appkit/nsstatusbar

https://developer.apple.com/documentation/foundation/userdefaults

1
  • Hi, thanks for your effort on this! Unfortunately the key part of my question is the repositioning and refreshing the menu bar icons, which you have not addressed in your answer. Writing the new preferences is very easy, as you've seen, but the unknown is how to refresh the positions - without restarting the app or macOS - in the way that macOS and Bartender do. Thanks again for your efforts. Aug 14, 2020 at 11:38
-3

I believe you are referring the invisble file called ".DS_STORE".

If you are command-line comfortable:

You can run this command to disable the writing of that file.

# Avoid creating .DS_Store files on network volumes
(sudo) defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true
# this should be run in your HOME directory. the "." means it will use the current folder.
# alternately you could test on one folder.
find . -type f -name '*.DS_Store' -ls -delete

All previous folders should have their ".DS_STORE" files deleted using this command.

After you delete these files, you can set the "Show View Options" under the view menu in the finder to how you want it, and "make default".

If you share directories with other mac users, their .DS_Store files will be created and affect your view. So it really is only effective in your personal environment.

1
  • Sorry, but your answer has absolutely nothing to do with the question asked! Aug 12, 2020 at 23:21

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .