📜 ⬆️ ⬇️

Serialize the settings using the NSCoding protocol

When developing almost any application, sooner or later there is a need to keep its settings, be it the current version or in-app settings of the application. What does the developer do in this case? It saves configuration data through NSUserDefaults and does it right.



When there are really a lot of settings, it becomes inconvenient to operate with them.
In the Peers.TV application, we used the following trick - archivers and NSCoding protocol. This helped us to unite some of the settings within one domain and to ease the work with them a little.

')
For reference. The Peers.TV application is designed to watch online TV and TV programs archive. Today it is 30 channels of various subjects. It has a program for the week ahead, transmission time reminders, in-app purchases, an archive - all this requires its own separate settings, which led us to the NSCoding protocol.

Consider the implementation of the NSCoding protocol using the example of blocking applications from unwanted access to third parties.
The implementation of the settings is made in a separate class-Singleton, and the settings themselves are implemented through the properties of this class.

Suppose we have 5 different settings, represented as follows:

@property (nonatomic, assign) BOOL isPasscodeEnabled; @property (nonatomic, assign) NSUInteger attemptCount; @property (nonatomic, assign) NSTimeInterval checkInterval; @property (nonatomic, strong) NSDate *nextCheckDate; @property (nonatomic, strong) NSString *applicationVersion; 


Accordingly, for each setting, we will need to announce an additional one key. The code responsible for initialization and implementation of properties is also omitted and will be listed below.

Now back to the NSCoding protocol. This protocol allows you to specify the order of serialization and deserialization of object data.

To implement this protocol, you must implement 2 methods:

 - (void)encodeWithCoder:(NSCoder *)aCoder; - (id)initWithCoder:(NSCoder *)aDecoder; 


We also need the NSKeyedArchiver and NSKeyedUnarchiver classes, which will do all the work for us. It is worth noting that these classes allow serialization and deserialization of data by keys.

Next, we describe the class that will combine the settings for blocking the application.

Interface:

 @interface PassCodeSettings: NSObject<NSCoding> @property (nonatomic, assign) BOOL isPasscodeEnabled; @property (nonatomic, assign) NSUInteger attemptCount; @property (nonatomic, assign) NSTimeInterval checkInterval; @property (nonatomic, strong) NSDate *nextCheckDate; @end 


Implementation:

 @implementation PassCodeSettings - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { _attemptCount = [aDecoder decodeIntegerForKey:@"attemptCount"]; _isPasscodeEnabled = [aDecoder decodeBoolForKey:@"isPassCodeEnabled"]; _checkInterval = [aDecoder decodeDoubleForKey:@"checkInterval"]; _nextCheckDate = [NSDate dateWithTimeIntervalSince1970:[aDecoder decodeDoubleForKey:@"nextCheckDate"]]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeBool:_isPasscodeEnabled forKey:@"isPassCodeEnabled"]; [aCoder encodeInteger:_attemptCount forKey:@"attemptCount"]; [aCoder encodeDouble:_checkInterval forKey:@"checkInterval"]; [aCoder encodeDouble:[_nextCheckDate timeIntervalSince1970] forKey:@"nextCheckDate"]; } @end 


Now the settings for blocking the application are placed in a separate class corresponding to the NSCoding protocol. Using NSKeyedArchiver, NSKeyedUnrachiver, we can implement the preservation of an arbitrary class in the settings.

Initialization and saving settings will look like this:

 - (id)init { self = [super init]; if (self) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; _applicationVersion = [defaults stringForKey:kApplicationVersion]; NSData *passCodeData = [defaults dataForKey:kPasscodeSettings]; if (passCodeData) { _passCodeSettings = [NSKeyedUnarchiver unarchiveObjectWithData:passCodeData]; } else { _passCodeSettings = [PassCodeSettings new]; } } return self; } - (void)savePasscodeSettings { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:_passCodeSettings] forKey:kPasscodeSettings]; [defaults synchronize]; } 


It is worth mentioning several techniques that can make your life easier:
1. In order not to save the settings manually using the savePasscodeSettings method, you can implement monitoring of properties according to KVO (Key Value Observing) principles or use application lifecycle notifications.
2. You can also send notification of changes to settings through the NSNotificationCenter.

Link to an interesting article about the NSCoding protocol from the notorious Mike Ash.

What we have achieved:
- Made some of the settings in one domain, now we can group the methods for working with domain data within this domain;
- A little easier to work with the settings.

Surprisingly, very few people use NSCoding for data serialization in their projects, although, in my opinion, it is undeserved. Of course, I can only judge this for open source projects.
This approach is not a silver bullet, it is an option among many others. It fits well with the structure of our application and facilitates our development.

That's all, the source code of this example is available here .

Our application Peers.TV, in which NSCoding was used, can be viewed on the AppStore:


Gleb Pinigin, iOS developer

Source: https://habr.com/ru/post/185918/


All Articles