#import #import NS_ASSUME_NONNULL_BEGIN typedef void (^HBPreferencesChangeCallback)(void); typedef void (^HBPreferencesValueChangeCallback)(NSString *key, id _Nullable value); /// The HBPreferences class in Cephei provides an interface for managing user-defined /// preferences of a tweak, and the default values used when the user has not yet changed a value. /// /// `HBPreferences` is very similar to `NSUserDefaults`, however it is specifically tailored to iOS /// tweak development, since tweaks may be loaded into a sandboxed process (most obviously, App /// Store apps, but also system apps like Safari), or one that runs as the `root` user (for /// instance, iFile, although these apps are slowly changing their model so they now run as mobile). /// In both of these cases, using `NSUserDefaults` will result in reading from preferences inside /// the sandbox, or inside `root`’s home directory; both of which are not what is expected. /// /// Advantages `HBPreferences` has over `NSUserDefaults` are: /// /// - Directly reading the property list from the `mobile` user’s home directory, to support /// sandboxed apps and apps running as `root`. /// - Intuitive method of setting a default preference value. /// - Updating of the app/tweak’s variables when preferences are changed. /// - Keyed subscripting is allowed, which enables simple array syntax. /// - Values in the preferences plist are called preferences, not defaults, to avoid ambiguity - /// `NSUserDefaults` uses “defaults” to refer to both preferences themselves and the fallback values /// if a key doesn’t exist. /// /// Ensure you read the discussion for `-registerObject:default:forKey:` before using the automatic /// updating mechanism. `-objectForKey:` does not update as another process updates the preferences /// on iOS 7 or older; if you need to support older iOS versions, use the registration methods /// instead. /// /// As of Cephei 1.17, HBPreferences supports Key-Value Observation. As such, you may subscribe to /// changes made to preferences through observer callbacks. The `-registerPreferenceChangeBlock:` /// and `-registerPreferenceChangeBlockForKey:block:` methods are provided to subscribe to /// preference changes via a callback block since Cephei 1.3, and you can additionally observe /// `HBPreferencesDidChangeNotification`. /// /// ### Example usage /// In Objective-C/Logos: /// /// ```logos /// HBPreferences *preferences; /// BOOL doThing; /// /// %ctor { /// preferences = [[HBPreferences alloc] initWithIdentifier:@"ws.hbang.common.demo"]; /// [preferences registerDefaults:@{ /// @"Enabled": @YES, /// @"AnotherSetting": @1.f /// }]; /// /// [preferences registerBool:&doThing default:NO forKey:@"DoThing"]; /// /// NSLog(@"Am I enabled? %i", [preferences boolForKey:@"Enabled"]); /// NSLog(@"Can I do thing? %i", doThing); /// } /// ``` /// /// In Swift: /// /// ```swift /// class Preferences { /// /// private let preferences = HBPreferences(identifier: "ws.hbang.common.demo") /// /// // Example using registration method /// private(set) var canDoThing: ObjCBool = false /// /// // Example using custom getter and setter /// var anotherSetting: Int { /// get { preferences["AnotherSetting"] as? Int ?? -1 } /// set { preferences["AnotherSetting"] = newValue } /// } /// /// // Example using KVO observation /// private var doThingObserver: NSKeyValueObserving? /// /// init() { /// preferences.register(defaults: [ /// "Enabled": true, /// "AnotherSetting": 1 /// ]) /// /// preferences.register(&canDoThing, default: false, forKey: "DoThing") /// /// print("Am I enabled? \(preferences["Enabled"] as? Bool ?? false)") /// print("Can I do thing? \(canDoThing)") /// } /// /// } /// ``` /// /// ### References /// * [NSUserDefaults in Practice](http://dscoder.com/defaults.html) /// /// ### Security /// As of Cephei 1.12, HBPreferences restricts most Apple preferences (identifiers starting with /// `com.apple.…`) from being read/written from a sandboxed process. This protects against a /// malicious app using HBPreferences as a way to gather sensitive information or change system /// preferences without the user’s knowledge. For instance, an App Store app could /// [phish for the user’s Apple ID login](https://krausefx.com/blog/ios-privacy-stealpassword-easily-get-the-users-apple-id-password-just-by-asking), /// creating a very real-looking login prompt by pre-filling their email address in the username box, /// or gain access to the numbers/email addresses of people the user has recently contacted. /// /// There is currently no way to avoid this restriction while still using HBPreferences. If you need /// access to Apple preferences, design your code to not need to do this from within the sandbox. /// This could be done [using IPC](http://iphonedevwiki.net/index.php/IPC) from an unsandboxed /// process such as SpringBoard. Avoid sending sensitive information via IPC to sandboxed apps, as /// they can still get access to data you send through various ways. @interface HBPreferences : NSObject /// @name Initializing an HBPreferences Object /// Creates an instance of the class for the specified identifier. /// /// @param identifier The identifier to be used. This is usually the same as the package identifier /// of the tweak. /// @return An autoreleased instance of HBPreferences for the specified identifier. + (instancetype)preferencesForIdentifier:(NSString *)identifier NS_SWIFT_UNAVAILABLE(""); /// Initializes an instance of the class for the specified identifier. /// /// @param identifier The identifier to be used. This is usually the same as the package identifier /// of the tweak. /// @return An autoreleased instance of HBPreferences for the specified identifier. - (instancetype)initWithIdentifier:(NSString *)identifier NS_DESIGNATED_INITIALIZER; /// The preferences identifier provided at initialisation. @property (nonatomic, retain, readonly) NSString *identifier; /// @name Synchronizing Preferences /// Synchronizes preferences data to prevent race conditions. /// /// On iOS 8.0 and later, waits until all communications between the `cfprefsd` daemon and the /// current process have completed, preventing race conditions and guaranteeing no data will be /// lost. Prior to iOS 8.0, writes all pending changes to disk, and reads latest preferences from /// disk. /// /// Deprecated. On iOS 12.0 and later, synchronization is /// [no longer required](https://developer.apple.com/documentation/ios-ipados-release-notes/foundation-release-notes#UserDefaults). /// The underlying CFPreferencesSynchronize() function simply returns `YES`. /// /// For earlier iOS releases, do not use this method directly unless you have a specific need. /// HBPreferences will synchronize automatically when needed. For further information on what this /// method does and when to use it, refer to /// [NSUserDefaults in Practice](http://dscoder.com/defaults.html) § “Sharing Defaults Between /// Programs”. /// /// @return `YES` if synchronization was successful, `NO` if an error occurred. - (BOOL)synchronize API_DEPRECATED("Synchronization is no longer required as of iOS 12", ios(5.0, 12.0)); /// @name Registering Default Preference Values /// The default preferences to be used when no value has been set by the user. /// /// You may modify the values of this dictionary directly. @property (nonatomic, copy, readonly) NSMutableDictionary *defaults; /// Adds the contents of the specified dictionary to the defaults property. /// /// Merges the provided dictionary with the mutable dictionary found on the defaults property. /// /// @param defaultValues The dictionary of keys and values you want to register. /// @see `defaults` - (void)registerDefaults:(NSDictionary *)defaultValues NS_SWIFT_NAME(register(defaults:)); /// @name Getting Preference Values /// Returns a dictionary that contains all preferences that are set. /// /// This does not include default values. /// /// @return A dictionary containing all keys and values. - (NSDictionary *)dictionaryRepresentation; /// Returns the object associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The object associated with the specified key. /// @warning You must manually synchronize preferences or use `-registerObject:default:forKey:` for /// this value to be updated when running on iOS 7 or older. - (id)objectForKey:(NSString *)key; /// Returns the integer value associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The integer value associated with the specified key. /// @see `-objectForKey:` - (NSInteger)integerForKey:(NSString *)key; /// Returns the unsigned integer value associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The unsigned integer value associated with the specified key. /// @see `-objectForKey:` - (NSUInteger)unsignedIntegerForKey:(NSString *)key; /// Returns the floating-point value associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The floating-point value associated with the specified key. /// @see `-objectForKey:` - (CGFloat)floatForKey:(NSString *)key; /// Returns the double value associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The double value associated with the specified key. /// @see `-objectForKey:` - (double)doubleForKey:(NSString *)key; /// Returns the Boolean value associated with the specified key. /// /// If the preference is not yet set, returns the default. If no default is set, returns `nil`. /// /// @param key The key for which to return the corresponding value. /// @return The Boolean value associated with the specified key. /// @see `-objectForKey:` - (BOOL)boolForKey:(NSString *)key; /// Returns the value associated with a given key. /// /// This method behaves the same as `-objectForKey:`, and enables the preferences object to be used /// with a subscript (square brackets). For example: /// /// Objective-C: /// /// ```objc /// NSString *fooBar = preferences[@"FooBar"]; /// preferences[@"Awesome"] = @YES; /// ``` /// /// Swift: /// /// ```swift /// let fooBar = preferences["FooBar"] as? String /// preferences["Awesome"] = true /// ``` /// /// @param key The key for which to return the corresponding value. /// @return The value associated with the specified key. /// @see `-objectForKey:` - (id)objectForKeyedSubscript:(id)key; /// Returns the object associated with the specified key, or if no user preference is set, the /// provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The object associated with the specified key, or the default value. - (id)objectForKey:(NSString *)key default:(nullable id)defaultValue; /// Returns the integer value associated with the specified key, or if no user preference is set, /// the provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The integer value associated with the specified key, or the default value. /// @see `-objectForKey:default:` - (NSInteger)integerForKey:(NSString *)key default:(NSInteger)defaultValue; /// Returns the unsigned integer value associated with the specified key, or if no user preference /// is set, the provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The unsigned integer value associated with the specified key, or the default value. /// @see `-objectForKey:default:` - (NSUInteger)unsignedIntegerForKey:(NSString *)key default:(NSUInteger)defaultValue; /// Returns the floating-point value associated with the specified key, or if no user preference is /// set, the provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The floating-point value associated with the specified key, or the default value. /// @see `-objectForKey:default:` - (CGFloat)floatForKey:(NSString *)key default:(CGFloat)defaultValue; /// Returns the double value associated with the specified key, or if no user preference is set, /// the provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The double value associated with the specified key, or the default value. /// @see `-objectForKey:default:` - (double)doubleForKey:(NSString *)key default:(double)defaultValue; /// Returns the Boolean value associated with the specified key, or if no user preference is set, /// the provided default. /// /// @param key The key for which to return the corresponding value. /// @param defaultValue The default value to use when no user preference is set. /// @return The Boolean value associated with the specified key, or the default value. /// @see `-objectForKey:default:` - (BOOL)boolForKey:(NSString *)key default:(BOOL)defaultValue; /// @name Setting Preference Values /// Sets the value of the specified key. /// /// You should only call these methods if you are certain that the process is running as the /// `mobile` user. /// /// @param value The object to store in the preferences. /// @param key The key with which to associate with the value. /// @exception HBPreferencesNotMobileException Thrown when the method is called by a process not /// running as the `mobile` user. - (void)setObject:(nullable id)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified integer value. /// /// This is a convenience method that calls `-setObject:forKey:`. See the discussion of that method /// for more details. /// /// @param value The integer value to store in the preferences. /// @param key The key with which to associate with the value. /// @see `-setObject:forKey:` - (void)setInteger:(NSInteger)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified unsigned integer value. /// /// This is a convenience method that calls `-setObject:forKey:`. See the discussion of that method /// for more details. /// /// @param value The unsigned integer value to store in the preferences. /// @param key The key with which to associate with the value. /// @see `-setObject:forKey:` - (void)setUnsignedInteger:(NSUInteger)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified floating-point value. /// /// This is a convenience method that calls `-setObject:forKey:`. See the discussion of that method /// for more details. /// /// @param value The floating-point value to store in the preferences. /// @param key The key with which to associate with the value. /// @see `-setObject:forKey:` - (void)setFloat:(CGFloat)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified double value. /// /// This is a convenience method that calls `-setObject:forKey:`. See the discussion of that method /// for more details. /// /// @param value The double value to store in the preferences. /// @param key The key with which to associate with the value. /// @see `-setObject:forKey:` - (void)setDouble:(double)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified Boolean value. /// /// This is a convenience method that calls `-setObject:forKey:`. See the discussion of that method /// for more details. /// /// @param value The Boolean value to store in the preferences. /// @param key The key with which to associate with the value. /// @see `-setObject:forKey:` - (void)setBool:(BOOL)value forKey:(NSString *)key NS_SWIFT_NAME(set(_:forKey:)); /// Sets the value of the specified key to the specified value. /// /// This method behaves the same as `-setObject:forKey:`, and enables the preferences object to be /// used with a subscript (square brackets). For example: /// /// ```objc /// NSString *fooBar = preferences[@"FooBar"]; /// preferences[@"Awesome"] = @YES; /// ``` /// /// @param object The value to store in the preferences. /// @param key The key with which to associate with the value. - (void)setObject:(nullable id)object forKeyedSubscript:(id)key; /// @name Removing Preference Values /// Removes a given key and its associated value from the dictionary. /// /// @param key The key to remove. - (void)removeObjectForKey:(NSString *)key NS_SWIFT_NAME(removeValue(forKey:)); /// Removes all stored preferences. /// /// This method acts in the same way as discussed in `-removeObjectForKey:`. - (void)removeAllObjects NS_SWIFT_NAME(removeAll()); /// @name Registering Variables /// Register an object to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// You must post a Darwin notification after updating preferences for this to work. In particular, /// it must be set to the value of identifier, followed by `/ReloadPrefs` - for instance, /// `ws.hbang.common.demo/ReloadPrefs`. In a Preferences specifier property list, you can use the /// `PostNotification` key on your specifiers to achieve this: /// /// ```xml /// /// … /// PostNotification /// ws.hbang.common.demo/ReloadPrefs /// /// ``` /// /// @param object The pointer to the object. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerObject:(_Nullable id __strong * _Nonnull)object default:(nullable id)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// Register an integer value to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// @param object The pointer to the integer. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerInteger:(NSInteger *)object default:(NSInteger)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// Register an unsigned integer value to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// @param object The pointer to the unsigned integer. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerUnsignedInteger:(NSUInteger *)object default:(NSUInteger)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// Register a floating-point value to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// @param object The pointer to the integer. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerFloat:(CGFloat *)object default:(CGFloat)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// Register a double value to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// @param object The pointer to the double. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerDouble:(double *)object default:(double)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// Register a Boolean value to be automatically set to the user’s preference. /// /// If the preference is not yet set, the object will be set to the provided default. /// /// @param object The pointer to the Boolean. /// @param defaultValue The default value to be used if no user preference is set. /// @param key The key in the preferences property list. /// @see `-registerObject:default:forKey:` - (void)registerBool:(BOOL *)object default:(BOOL)defaultValue forKey:(NSString *)key NS_SWIFT_NAME(register(_:default:forKey:)); /// @name Preference Change Callbacks /// Register a block to be called when a preference change is detected. /// /// Blocks are called after HBPreferences’ cache of values is updated. The block will also be called /// immediately after calling this method. See `registerObject:default:forKey:` for details on how /// to set up callbacks. /// /// @param callback A block object called when the specified key’s value changes. The block object /// takes no parameters and returns no value. /// @see `-registerObject:default:forKey:` - (void)registerPreferenceChangeBlock:(HBPreferencesChangeCallback)callback; /// Register a block to be called when a specific preference is changed. /// /// Blocks are called after HBPreferences’ cache of values is updated. The block will also be called /// immediately after calling this method. See `registerObject:default:forKey:` for details on how /// to set up callbacks. /// /// @param key The key to listen for. /// @param callback A block object called when the specified key’s value changes. The block object’s /// parameters are the key and its new value. /// @see `-registerObject:default:forKey:` - (void)registerPreferenceChangeBlockForKey:(NSString *)key block:(HBPreferencesValueChangeCallback)callback; /// Register a block to be called when a specific preference is changed. /// /// Blocks are called after HBPreferences’ cache of values is updated. The block will also be called /// immediately after calling this method. See `-registerObject:default:forKey:` for details on how /// to set up callbacks. /// /// Deprecated. This method signature changed in Cephei 1.17 to better support the order of /// arguments preferred by Swift closure syntax. Use `-registerPreferenceChangeBlockForKey:block:` /// instead. /// /// @param callback A block object called when the specified key’s value changes. The block object’s /// parameters are the key and its new value. /// @param key The key to listen for. /// @see `-registerPreferenceChangeBlockForKey:block:` /// @see `-registerObject:default:forKey:` - (void)registerPreferenceChangeBlock:(HBPreferencesValueChangeCallback)callback forKey:(NSString *)key __attribute((deprecated("Use registerPreferenceChangeBlockForKey:block: instead."))); @end /// Name of an exception that occurs when attempting to set preferences from a process not running /// as the `mobile` user. extern NSExceptionName const HBPreferencesNotMobileException NS_SWIFT_NAME(HBPreferences.notMobileException); /// This notification is posted when a change is made to a registered preferences identifier. The /// notification object is the associated HBPreferences object. extern NSNotificationName const HBPreferencesDidChangeNotification NS_SWIFT_NAME(HBPreferences.didChangeNotification); NS_ASSUME_NONNULL_END