Getting Started
This walks through a tweak that does nothing but prove it loaded. Once that works, everything else is detail.
Before you start
TweakInject needs System Integrity Protection disabled, because injecting
into a running process requires task_for_pid, which SIP blocks.
The app's Station Board reports what is missing and what to do about it. See
Installation for the host setup.
The tweak
#import <Foundation/Foundation.h>
__attribute__((constructor))
static void init(void) {
NSLog(@"[MyTweak] loaded into %@",
[[NSProcessInfo processInfo] processName]);
}
A constructor runs when the library is loaded, which is the moment the loader has put you inside the host process.
The filter
Without one, your tweak loads into nothing. This targets the Dock:
<?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>
</dict>
</plist>
Building
clang -dynamiclib -arch arm64e \
-isysroot "$(xcrun --show-sdk-path --sdk macosx)" \
-framework Foundation \
-install_name /Library/TweakInject/Tweaks/DynamicLibraries/MyTweak.dylib \
-o MyTweak.dylib MyTweak.m
codesign -f -s - MyTweak.dylib
The signature is ad-hoc. Library validation is disabled system-wide, which is the only reason anything not signed by Apple loads into these processes at all.
Installing it
Drag the .dylib and its .plist onto the Installed
Tweaks page, or package them as a .deb and install that. See
Packaging.
Restart the Dock and watch the log. Tweaks are injected at process start, so anything already running has to be restarted before it picks one up.
Next
- Filters, to target something more precisely.
- Preferences, to make it configurable.
- Safe Mode, for when a tweak breaks something.