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.

Root.plist
<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

Info.plist keys
<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 into the app. That is why the app requests the entitlement to disable library validation: your bundle is signed by you, not by this project, and validation would otherwise refuse it. Declarative panes are data and are unaffected.

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.