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 requires System Integrity Protection to be disabled, which greatly reduces your Mac's security. See Installation.

The tweak

MyTweak.m
#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:

Filter.plist
<?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

Build
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