m (→Late hooking) |
(→Late hooking: Block API) |
||
Line 222: | Line 222: | ||
name:NSBundleDidLoadNotification | name:NSBundleDidLoadNotification | ||
object:nil]; | object:nil]; | ||
} | |||
</source> | |||
If you have a function you need to call, the block API can help evade creating an object to handle the notification. The following snippet creates a similar usage of the <tt>CFNotificationCenterAddObserver</tt> API. | |||
<source lang="objc"> | |||
static void notificationCallback(NSNotificationCenter *center, id observer, NSString *name, id object, NSDictionary *userInfo) { | |||
if ([notification.userInfo[NSLoadedClasses] containsString:@"targetClass"]) { | |||
// Target class has been loaded | |||
} | |||
} | |||
static id observer; | |||
%ctor { | |||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; | |||
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; | |||
observer = [center addObserverForName:NSBundleDidLoadNotification object:nil queue:mainQueue | |||
usingBlock:^(NSNotification *notification) { | |||
notificationCallback(center, observer, [notification name], [notification object], [notification userInfo]); | |||
}]; | |||
} | |||
//if a destructor existed or you don't need to track the notification anymore | |||
%dtor { | |||
[[NSNotificationCenter defaultCenter] removeObserver:observer]; | |||
} | } | ||
</source> | </source> |
Revision as of 23:45, 5 December 2014
Find me on saurik's IRC server, Twitter, Reddit, Github, StackOverflow.
Made UnlockEvents and FlipNC.
If you are looking for notifications within a process, give NotificationExplorer a look.
And a repository there: uroboro.github.io/repo.
"Interesting" links
How 2 prefs
As opposed to using NSDictionaries and [NSHomeDirectory() stringByAppendingFormat:@"/Library/Preferences/%s.plist", "com.your.tweak"]
, here's another alternative to handling preferences on tweaks.
To do: read [this] carefully and move all information regarding preferences to a more appropriate place.
Tweak.xm
@interface NSUserDefaults (Tweak_Category)
- (id)objectForKey:(NSString *)key inDomain:(NSString *)domain;
- (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain;
@end
static NSString *nsDomainString = @"com.your.tweak";
static NSString *nsNotificationString = @"com.your.tweak/preferences.changed";
static BOOL enabled;
static void notificationCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
NSNumber *n = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"enabled" inDomain:nsDomainString];
enabled = (n)? [n boolValue]:YES;
}
%ctor {
// Set variables on start up
notificationCallback(NULL, NULL, NULL, NULL, NULL);
// Register for 'PostNotification' notifications
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), NULL, notificationCallback, (CFStringRef)nsNotificationString, NULL, CFNotificationSuspensionBehaviorCoalesce);
// Add any personal initializations
}
/*
* From here onward, write your tweak.
* To make your tweak actually do stuff when enabled:
if (!enabled) {
// Do the original algorithm, either by calling:
// %orig(); //if using logos for methods
// _functionName(args); //if using MSHook() for functions
} else {
...
// Optionally, do the original algorithm
}
*/
Preference plist
Choose any of the following:
Reduced style
Provides a switch on the root section of the preferences (like Airplane Mode). Recommended for configuration-less tweaks.
Saved in your tweak's folder as layout/Library/PreferenceLoader/Preferences/com.your.tweak.plist
{ entry = { cell = PSSwitchCell; defaults = "com.your.tweak"; label = "Your Tweak"; key = enabled; default = 1; icon = "/Applications/Preferences.app/[email protected]"; PostNotification = "com.your.tweak/preferences.changed"; }; }
Extended Style
Provides a pane where other cells can appear (like Wi-Fi). Recommended for configuration-friendly tweaks.
Saved in your tweak's folder as layout/Library/PreferenceLoader/Preferences/com.your.tweak.plist
{ title = "Your Tweak"; entry = { cell = PSLinkCell; label = "Your Tweak"; icon = "/Applications/Preferences.app/[email protected]"; }; items = ( { cell = PSSwitchCell; defaults = "com.your.tweak"; label = Enabled; key = enabled; default = 1; PostNotification = "com.your.tweak/preferences.changed"; } // add more cells (dictionaries) here ); }
PreferenceLoader Style
Provides a static list of cells. Recommended for Preference Bundles of tweaks.
Saved in your tweak's Preference Bundle subproject folder as Resources/com.your.tweak.plist
{ items = ( { cell = PSSwitchCell; defaults = "com.your.tweak"; label = Enabled; key = enabled; default = 1; PostNotification = "com.your.tweak/preferences.changed"; } // add more cells (dictionaries) here ); }
Flipswitches
After using the Flipswitch NIC template, modify accordingly
Switch.x
#import "FSSwitchDataSource.h"
#import "FSSwitchPanel.h"
@interface NSUserDefaults (Tweak_Category)
- (id)objectForKey:(NSString *)key inDomain:(NSString *)domain;
- (void)setObject:(id)value forKey:(NSString *)key inDomain:(NSString *)domain;
@end
static NSString *nsDomainString = @"com.your.tweak";
static NSString *nsNotificationString = @"com.your.tweak/preferences.changed";
@interface YourTweakFlipswitchSwitch : NSObject <FSSwitchDataSource>
@end
@implementation YourTweakFlipswitchSwitch
- (NSString *)titleForSwitchIdentifier:(NSString *)switchIdentifier {
return @"Your Tweak";
}
- (FSSwitchState)stateForSwitchIdentifier:(NSString *)switchIdentifier {
NSNumber *n = (NSNumber *)[[NSUserDefaults standardUserDefaults] objectForKey:@"enabled" inDomain:nsDomainString];
BOOL enabled = (n)? [n boolValue]:YES;
return (enabled) ? FSSwitchStateOn : FSSwitchStateOff;
}
- (void)applyState:(FSSwitchState)newState forSwitchIdentifier:(NSString *)switchIdentifier {
switch (newState) {
case FSSwitchStateIndeterminate:
break;
case FSSwitchStateOn:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:@"enabled" inDomain:nsDomainString];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)nsNotificationString, NULL, NULL, YES);
break;
case FSSwitchStateOff:
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:NO] forKey:@"enabled" inDomain:nsDomainString];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (CFStringRef)nsNotificationString, NULL, NULL, YES);
break;
}
return;
}
@end
Late hooking
If you need to use UI elements, you have to wait until the application is ready.
...
- (void)receiveNotification:(NSNotification *)notification {
// UI is available
}
...
%ctor {
[[NSNotificationCenter defaultCenter] addObserver:[UFSSomeClass sharedInstance]
selector:@selector(didReceiveNotification:)
name:UIApplicationDidFinishLaunchingNotification
object:nil];
}
Same applies if a class you want to hook is created or loaded dynamically.
...
- (void)receiveNotification:(NSNotification *)notification {
if ([notification.userInfo[NSLoadedClasses] containsString:@"targetClass"]) {
// Target class has been loaded
}
}
...
%ctor {
[[NSNotificationCenter defaultCenter] addObserver:[UFSSomeClass sharedInstance]
selector:@selector(didReceiveNotification:)
name:NSBundleDidLoadNotification
object:nil];
}
If you have a function you need to call, the block API can help evade creating an object to handle the notification. The following snippet creates a similar usage of the CFNotificationCenterAddObserver API.
static void notificationCallback(NSNotificationCenter *center, id observer, NSString *name, id object, NSDictionary *userInfo) {
if ([notification.userInfo[NSLoadedClasses] containsString:@"targetClass"]) {
// Target class has been loaded
}
}
static id observer;
%ctor {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
observer = [center addObserverForName:NSBundleDidLoadNotification object:nil queue:mainQueue
usingBlock:^(NSNotification *notification) {
notificationCallback(center, observer, [notification name], [notification object], [notification userInfo]);
}];
}
//if a destructor existed or you don't need to track the notification anymore
%dtor {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
}
Other Stuff
Similar Wanted Pages:
Test Zone
Wondering what looks better:
To "wikitable"...
JSON style | XML style |
---|---|
{
entry = {
cell = PSSwitchCell;
defaults = "com.your.tweak";
label = "Your Tweak";
key = enabled;
default = 1;
icon = "/Applications/Preferences.app/[email protected]";
PostNotification = "com.your.tweak/preferences.changed";
};
}
|
<?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>entry</key>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>defaults</key>
<string>com.your.tweak</string>
<key>label</key>
<string>Your Tweak</string>
<key>key</key>
<string>enabled</string>
<key>default</key>
<true/>
<key>icon</key>
<string>/Applications/Preferences.app/[email protected]</string>
<key>PostNotification</key>
<string>com.your.tweak/preferences.changed</string>
</dict>
</dict>
</plist>
|
or not to "wikitable"...
JSON style | XML style |
---|---|
{
entry = {
cell = PSSwitchCell;
defaults = "com.your.tweak";
label = "Your Tweak";
key = enabled;
default = 1;
icon = "/Applications/Preferences.app/[email protected]";
PostNotification = "com.your.tweak/preferences.changed";
};
}
|
<?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>entry</key>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>defaults</key>
<string>com.your.tweak</string>
<key>label</key>
<string>Your Tweak</string>
<key>key</key>
<string>enabled</string>
<key>default</key>
<true/>
<key>icon</key>
<string>/Applications/Preferences.app/[email protected]</string>
<key>PostNotification</key>
<string>com.your.tweak/preferences.changed</string>
</dict>
</dict>
</plist>
|