search
LoginSignup
1

More than 5 years have passed since last update.

posted at

Flipswitchによる設定(PreferenceLoader)のリアルタイム更新

Flipswiitchを使ってTweakのON/OFFを行う機会があると思います。その時にFlipswitchと設定app(PreferenceLoader)間でリアルタイムに同期する方法を紹介します。

基本的な書き方

https://github.com/ichitaso/UIRotation9/blob/master/UIRotationSwitch/Switch.x

Switch.x
#define PREF_PATH @"/var/mobile/Library/Preferences/com.ichitaso.uirotation9.plist"
#define kPrefKey @"enabled"

@interface UIRotationSwitch : NSObject <FSSwitchDataSource>
@end

@implementation UIRotationSwitch

- (FSSwitchState)stateForSwitchIdentifier:(NSString *)switchIdentifier
{
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:PREF_PATH];

    id enable = [dict objectForKey:kPrefKey];
    BOOL isEnabled = enable ? [enable boolValue] : YES;

    return isEnabled ? FSSwitchStateOn : FSSwitchStateOff;
}

これで、「/var/mobile/Library/Preferences/com.ichitaso.uirotation9.plist」内の「enabled」keyのBOOL値によってFlipswitchのON/OFF時の動作が適用されます。

Flipswitch側での設定のON/OFF

Switch.x
- (void)applyState:(FSSwitchState)newState forSwitchIdentifier:(NSString *)switchIdentifier
{
    NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:PREF_PATH];

    NSMutableDictionary *mutableDict = dict ? [[dict mutableCopy] autorelease] : [NSMutableDictionary dictionary];

    switch (newState) {
        case FSSwitchStateIndeterminate:
            return;
        case FSSwitchStateOn:
            [mutableDict setValue:@YES forKey:kPrefKey];
            CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.ichitaso.uirotation-switchOn"), NULL, NULL, true);
            break;
        case FSSwitchStateOff:
            [mutableDict setValue:@NO forKey:kPrefKey];
            CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.ichitaso.uirotation-switchOff"), NULL, NULL, true);
            break;
    }

    [mutableDict writeToFile:PREF_PATH atomically:YES];

    // Update Flipswitch state
    [[FSSwitchPanel sharedPanel] stateDidChangeForSwitchIdentifier:@"com.ichitaso.uirotationfs"];
}

ここがFlipswitchを動かした時に動作する部分です。

「NSMutableDictionary」によってplistファイルの書き込みと「CFNotificationCenterPostNotification」で通知を行います。

ONにするときは「FSSwitchStateOn」

Switch.x
[mutableDict setValue:@YES forKey:kPrefKey];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.ichitaso.uirotation-switchOn"), NULL, NULL, true);

OFFは「FSSwitchStateOff」

Switch.x
[mutableDict setValue:@NO forKey:kPrefKey];
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFSTR("com.ichitaso.uirotation-switchOff"), NULL, NULL, true);

最後にplistファイルに書き込みと、Switchの反映を行います。

Switch.x
[mutableDict writeToFile:PREF_PATH atomically:YES];

// Update Flipswitch state
[[FSSwitchPanel sharedPanel] stateDidChangeForSwitchIdentifier:@"com.ichitaso.uirotationfs"];

通知の処理

このままでは通知が反映されないので、少し工夫します。Tweak側で通知を受けた時に、PreferenceLoaderでも反映されるようにします。

UIRController.h
@interface UIRController : NSObject
{
    NSUserDefaults *prefs;
}

@property (nonatomic) BOOL prefsChangedFromSettings;
@property (nonatomic) BOOL enable;
+ (UIRController *)sharedInstance;
- (void)updateSettings;
- (void)setEnabled:(BOOL)enable;

@end
UIRController.m
#import "UIRController.h"

#define PREF_PATH @"/var/mobile/Library/Preferences/com.ichitaso.uirotation9.plist"
#define Preferences @"com.ichitaso.uirotation9"

@implementation UIRController
+ (UIRController *)sharedInstance {
    static dispatch_once_t p = 0;
    __strong static id _sharedObject = nil;
    dispatch_once(&p, ^{
        _sharedObject = [[self alloc] init];
    });
    return _sharedObject;
}

- (id)init {
    if (self = [super init]) {
        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:PREF_PATH];
        id isEnabled = [dict objectForKey:@"enabled"];
        BOOL enabled = isEnabled ? [isEnabled boolValue] : YES;

        prefs = [[NSUserDefaults alloc] initWithSuiteName:Preferences];

        [prefs registerDefaults:@{
            @"enable": @YES
            }];

        [prefs setBool:enabled forKey:@"enabled"];

        _prefsChangedFromSettings = NO;
        _enable = enabled;

    }
    return self;
}

- (void)updateSettings
{
    [self setEnabled:[prefs boolForKey:@"enabled"]];
    _prefsChangedFromSettings = NO;
}

- (void)setEnabled:(BOOL)enable
{
    if (enable == _enable)
        return;

    _enable = enable;

    if (!_prefsChangedFromSettings)
        [prefs setBool:_enable forKey:@"enabled"];
}

@end

このようなファイルを用意してTweak.xmと一緒にcompileするようにします。

Tweak.xm
// Called by the flipswitch toggle
//==============================================================================

#import "UIRController.h"

void prefsChanged() {
    [UIRController sharedInstance].prefsChangedFromSettings = YES;
    [[UIRController sharedInstance] updateSettings];

}

void switchToggleOn() {
    [[UIRController sharedInstance] setEnabled:YES];
}

void switchToggleOff() {
    [[UIRController sharedInstance] setEnabled:NO];
}

//==============================================================================

%ctor
{
    @autoreleasepool {
...
       CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                        NULL,
                                        (CFNotificationCallback)switchToggleOn,
                                        CFSTR("com.ichitaso.uirotation-switchOn"),
                                        NULL,
                                        CFNotificationSuspensionBehaviorDeliverImmediately);

        CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                        NULL,
                                        (CFNotificationCallback)switchToggleOff,
                                        CFSTR("com.ichitaso.uirotation-switchOff"),
                                        NULL,
                                        CFNotificationSuspensionBehaviorDeliverImmediately);
...

これでdylib側とPreferenceLoaderで設定がリンクされるようになりました。

設定(PreferenceLoader)

https://github.com/ichitaso/UIRotation9/blob/master/Preference.m

Preference.m
@interface UIRPreferenceController : PSListController <UIActionSheetDelegate, UIAlertViewDelegate>
{
}
- (void)reloadPrefs:(NSNotification *)notification;
@end

NSNotificationクラスを使用します。

Preference.m
- (instancetype)init
{
    self = [super init];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:@"reloadPrefs" object:nil];

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                    NULL,
                                    (CFNotificationCallback)reloadPrefsCallBack,
                                    CFSTR("com.ichitaso.uirotation-switchOn"),
                                    NULL,
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(),
                                    NULL,
                                    (CFNotificationCallback)reloadPrefsCallBack,
                                    CFSTR("com.ichitaso.uirotation-switchOff"),
                                    NULL,
                                    CFNotificationSuspensionBehaviorDeliverImmediately);

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reloadPrefs:) name:@"reloadPrefs" object:nil];

    return self;
}

「- (instancetype)init」で、通知処理を追加します。

Preference.m
void reloadPrefsCallBack() {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"reloadPrefs" object:nil];
}

- (void)reloadPrefs:(NSNotification *)notification
{
    [self reloadSpecifiers];
}

これで通知を受けた際に[self reloadSpecifiers];で設定の更新を行うようにすると、Flipswitch側で操作した時に設定app側でもリアルタイムでスイッチがON/OFFされるようになりました。

細かい部分ですが、ちょっとしたこだわりですね!

それではまた。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1