LoginSignup
1
1

More than 5 years have passed since last update.

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

Posted at

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されるようになりました。

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

それではまた。

1
1
0

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
  3. You can use dark theme
What you can do with signing up
1
1