Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSUserDefaults transactionality

Is there any way to add transactionality to NSUserDefaults? I would need something like the well known begin - commit - revert functions on database handlers, thus I could revert a modification on the user defaults in some cases. Of course other users of this user defaults would be blocked from writing during the transaction.

Note1: synchronize method of the above class does not do this thing because:

  1. according to the doc, it is called from time to time also by the framework
  2. there is no "revert"

Note2: I saw dictionaryRepresentation and registerDefaults with that I could implement my own transaction mechanism (holding a copy of the old defaults in the memory / even saved to a plist during the transaction). But maybe there is a ready solution for this?

My use case:

I have a wizard-like flow of screens where the user can edit some settings on each screen. As of the current implementation these settings are stored immediately in the defaults as the user moves to the next screen of the wizard. Now this wizard can be interrupted by some other events (even the user can choose to exit/cancel the wizard at any screen) and in this case I would like to roll back the modifications.

like image 376
MrTJ Avatar asked May 21 '26 00:05

MrTJ


1 Answers

One possible solution is to defer setting the values until the end of your wizard. This can be easily done for example using a proxy that will record the messages sent to it and then replay them on the real NSUserDefaults. Recording the messages should be pretty simple:

- (void) forwardInvocation: (NSInvocation*) invocation
{
    [invocations addObject:invocation];
}

Where invocations is a mutable array. Replaying the messages back is also simple:

- (void) replayOnTarget: (id) target
{
    for (NSInvocation *op in invocations)
        [op invokeWithTarget:target];
}

This way the wizard does not have to know anything about the transactions. It would get the recording proxy instead of the expected NSUserDefaults instance and send the messages as usual. After the calling code knows the wizard succeeded, it can replay the messages from the proxy on the shared user defaults. (I have added some sample code on GitHub.)

Maybe this is overkill, but since the recording proxy is generic and can be used in other cases, maybe it’s not bad. Same thing can also be done using blocks:

[transaction addObject:[^{
    [defaults setObject:… forKey:…];
} copy]];

Where transaction is a mutable array, again. When the wizard succeeds, you would simply iterate over the array and execute the stored blocks.

like image 90
zoul Avatar answered May 23 '26 15:05

zoul



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!