Call function when if value in NSUserDefaults.standardUserDefaults() changes
First
NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "Gift", options: NSKeyValueObservingOptions.New, context: nil)
Second
deinit {
NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: "Gift")
}
Third
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
setGiftCount()
}
iOS NSUserDefaults Watching the change of values for a single key
Edit: viewDidUnload
is now deprecated, use dealloc
instead to removeObserver
This should work perfectly, I have just tested here.
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"SomeKey"
options:NSKeyValueObservingOptionNew
context:NULL];
// Testing...
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"test" forKey:@"SomeKey"];
[defaults synchronize];
}
- (void)viewDidUnload
{
[super viewDidUnload];
[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"SomeKey"];
}
- (void)observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
if([keyPath isEqual:@"SomeKey"])
{
NSLog(@"SomeKey change: %@", change);
}
}
Things you could test.
Put a break point in viewDidUnload and make sure the view isn't disappearing on you (since you are changing
SomeKey
from another viewController) If this is the case then maybe move your register/de-register code into init/dealloc depending on how your VC works.Use explicit KeyPath like
@"SomeKey"
not a substitution likeSOME_NSSTRING_VARIABLE
How to detect changes in NSUserDefault?
You can post a notification whenever you call synchronize
[[NSNotificationCenter defaultCenter] postNotificationName:@"MyAppSettingsChanged" object:self userInfo:nil];
Then in your other class listen to the notification.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppSettingsChanged:) name:@"MyAppSettingsChanged" object:nil];
-(void) onAppSettingsChanged:(NSNotification)notification
{
// settings changed
}
If you want, you can pass an NSDictionary
into userInfo when calling postNotificationName
that contains information like which settings have changed.
Detect changes on NSUserDefaults
try out the NSUserDefaultsDidChangeNotification
with this code snippet:
- (id)init {
self = [super init];
if(self) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification
object:nil];
}
return self;
}
- (void)defaultsChanged:(NSNotification *)notification {
// Get the user defaults
NSUserDefaults *defaults = (NSUserDefaults *)[notification object];
NSLog(@"%@", [defaults objectForKey:@"yourIntrestedObject"]);
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Cocoa - Notification on NSUserDefaults value change?
Spent all day looking for the answer, only to find it 10 minutes after asking the question...
Came across a solution through Key-Value-Observing:
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self
forKeyPath:@"values.MyPreference"
options:NSKeyValueObservingOptionNew
context:NULL];
Or, more simply (per comment below):
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"MyPreference"
options:NSKeyValueObservingOptionNew
context:NULL];
How to change the default return value of an NSUserDefault without saving it every time the app is run
It isn't totally clear what you are asking. Are you asking how to make it so that when your app is first installed and run, the value of your BOOL loads as true?
The answer is to use the registerDefaults()
method (as Martin R mentioned in his comment).
You pass it an NSDictionary containing the key/value pairs that you want to load as your "default defaults". The best place to do that is in the initialize class method for your app delegate. That way the default defaults key value pairs get set before any of your apps other code gets run.
So add this method to your app delegate:
class func initialize()
{
NSUserDefaults.standardUserDefaults().registerDefaults(
["myDefault": true,
"aStringDefault": "defaultStringValue"],
"arrayDefault": [1, 2, 3,])
//Do any other startup initialization that needs to happen
//before all your other app delegate code.
}
(Not tested. May need minor changes to work...)
EDIT:
You can register as many defaults as you like, and also write more complex structures into defaults. I updated the sample code to show a more complex example)
How to use KVO for UserDefaults in Swift?
As of iOS 11 + Swift 4, the recommended way (according to SwiftLint) is using the block-based KVO API.
Example:
Let's say I have an integer value stored in my user defaults and it's called greetingsCount
.
First I need to extend UserDefaults
with a dynamic var
that has the same name as the user defaults key you want to observe:
extension UserDefaults {
@objc dynamic var greetingsCount: Int {
return integer(forKey: "greetingsCount")
}
}
This allows us to later on define the key path for observing, like this:
var observer: NSKeyValueObservation?
init() {
observer = UserDefaults.standard.observe(\.greetingsCount, options: [.initial, .new], changeHandler: { (defaults, change) in
// your change logic here
})
}
And never forget to clean up:
deinit {
observer?.invalidate()
}
Related Topics
Changing Tab Bar Color (Swift)
Avplayerviewcontroller Black Screen When Swiping on iOS 11
Is .Playground a Swift File? Who Can 'See' It
Convert Optional String to Int in Swift
How to Insert a Sublayer in Swift
How to Restrict an Enum to Certain Cases of Another Enum
Core Image Filter Cisourceovercompositing Not Appearing as Expected with Alpha Overlay
How to Split a String into a [String] and Not [Substring]
Unwrapping Optional in Swiftui View
Swift Any Difference Between Closures and First-Class Functions
"Raw Value for Enum Case Is Not Unique" for Swift Enum with Float Raw Values
iOS 13.1 Cannot Save File to App Directory
Gcdasyncsocket Multiple Connections Wont Accept Data from Multiple Sockets