Preference Bundles
A preference bundle is how your tweak gets a settings pane. There are two kinds: declarative, described entirely by a plist, and a view controller you write yourself.
Bundles install to
/Library/TweakInject/Preferences/PreferenceBundles/. Declare a
dependency on com.doraorak.preferenceloaderx if you want the
pane to appear in System Settings as well as in the app.
Declarative
A Root.plist describing rows. No code, so nothing to load and
nothing to crash. Prefer this unless you need a control the format cannot
express.
<plist version="1.0">
<dict>
<key>title</key><string>MyTweak</string>
<key>items</key>
<array>
<dict>
<key>cell</key><string>PSSwitchCell</string>
<key>label</key><string>Enabled</string>
<key>key</key><string>enabled</string>
<key>default</key><true/>
</dict>
<dict>
<key>cell</key><string>PSSliderCell</string>
<key>label</key><string>Intensity</string>
<key>key</key><string>intensity</string>
<key>min</key><real>0.0</real>
<key>max</key><real>1.0</real>
<key>default</key><real>0.5</real>
</dict>
</array>
</dict>
</plist>
The key of each row is the preference key your tweak reads. The
domain comes from the bundle's Info.plist.
Info.plist
<key>CFBundleIdentifier</key><string>com.example.mytweakprefs</string> <key>NSPrincipalClass</key><string>MyPrefsController</string> <key>title</key><string>MyTweak</string> <key>defaults</key><string>com.example.mytweak</string> <key>icon</key><string>icon.svg</string>
defaults is the domain. It has to be the same string your tweak
reads from, or the pane writes where nothing is looking.
NSPrincipalClass is only needed for a custom pane.
Custom panes
Write a view controller and adopt PSPreferenceController, which tells you the domain and when the pane appears and disappears.
A custom pane is loaded as code, so a mistake in one can take the settings window down with it. A declarative pane cannot, which is the main reason to prefer one.
Where your bundle has to be
PreferenceLoaderX looks in two places:
/Library/TweakInject/Preferences/PreferenceBundles//Library/TweakInject/Tweaks/Bundles/, where your tweak already is
In each, it checks a bundle for Root.plist,
Resources/Root.plist or Contents/Resources/Root.plist.
So a declarative pane needs no separate bundle at all: put
Root.plist in your tweak's own
Contents/Resources/ and it is found where it sits.
What does not work is nesting a whole
.bundle inside your tweak bundle and expecting it to be picked
up. The search does not descend into a bundle within a bundle. A separate
preference bundle has to be installed to
PreferenceBundles/ by your .deb, which is what you
want anyway for a custom view-controller pane.
Reading the values back
From the tweak, use PSUserDefaults with the same domain, and register the same defaults the plist declares so both sides agree before anything has been written.