Filters
A filter is what stops your tweak being loaded into every process on the
machine. For a plain dynamic library it is a .plist of the same
name sitting beside it. For a bundle there are several places it can live,
and the loader checks them in a fixed order.
Where the filter lives in a bundle
The first of these that exists is the one used, so a file earlier in the list wins over anything later:
| # | Path inside the bundle | Notes |
|---|---|---|
| 1 | Contents/Info.plist |
Only if it has a Filter dictionary. Checked before everything else. |
| 2 | Contents/Resources/Filter.plist | The usual place. |
| 3 | Contents/Filter.plist | |
| 4 | Contents/Info.plist |
Again, this time accepted even without a Filter key. |
Putting the filter in Info.plist keeps a bundle down to one
plist. Keeping it in Contents/Resources/Filter.plist keeps it
separate from the bundle's own metadata. Either is fine; just do not ship
both, because Info.plist will win and the other will look like
it is being ignored for no reason.
Keys and accepted values
| Key | Type | Accepted values and matching |
|---|---|---|
Bundles | array | Bundle identifiers. Exact match, case-insensitive. Matches the main bundle or any bundle already loaded in the process, so a framework identifier matches too. |
Executables | array | Executable file names. Exact match on the last path component, case-insensitive. Not a substring match. |
Classes | array | Objective-C class names. Matches when the class exists in the host. Exact and case-sensitive. |
ExcludeBundles | array | Bundle identifiers that must never match. Evaluated before everything else; same comparison as Bundles. |
Type | string | Only App and Binary are evaluated. App requires the host to be an application, Binary requires that it is not. Any other value, including Any, imposes no restriction. |
Privilege | string | Only Root and User are evaluated, case-insensitively, against the effective user ID. Root requires euid 0, User requires non-zero. |
CoreFoundationVersion | array | One or two numbers, [minimum, maximum). The minimum is inclusive, the maximum exclusive. Elements past the second are ignored. |
How the keys combine
Bundles, Executables and Classes are
alternatives: any one matching is sufficient. Type,
Privilege, CoreFoundationVersion and
ExcludeBundles are restrictions applied on top of them.
An empty filter matches nothing. A filter with only Type or only
Privilege matches every process that passes it, which is allowed
and occasionally what you want. Be deliberate about it: loading into
everything is a lot of processes.
Example
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Bundles</key>
<array>
<string>com.apple.dock</string>
</array>
<key>Privilege</key>
<string>User</string>
</dict>
</plist>