Jimmy
Jimmy

Reputation: 10341

Storing arbitrary keys/values in iOS

Is there a way to store arbitrary keys/values in iOS, I know I could create column that store JSON key-value in SQLite but I want something more efficient so I could be able to get only the values that I need from a queried record.

Preferably If I could do this without having external database (like LevelDB) but if you think this might be the only way, let me know which one you prefer.

Update:

The data will hold articles (long html articles, title and date, and other undefined data) retrieved from the server and other meta data, it will be retrieved and displayed every time the user opens the app. (thats why NSUserDefaults will not work for this use case)

Upvotes: 16

Views: 4357

Answers (7)

Qiulang
Qiulang

Reputation: 12405

YapDatabase can be another option if you just want to use sqlite

It provide API like this, which seems ideal for your use case.

- (void)setObject:(nullable id)object
           forKey:(NSString *)key
     inCollection:(nullable NSString *)collection
     withMetadata:(nullable id)metadata;

Upvotes: 0

Jay Zhao
Jay Zhao

Reputation: 956

I've written an App that deals with 50MB texts(about 10,000 articles in HTML). They're indexed by an articleID. I've used LevelDB, raw Sqlite, Sqlite with FMDB, CoreData and NSUserDefaults in this App.

Here's what I've learned:

LevelDB gives you the arbitrary key/value access pattern and the reading speed is extremely fast if you're reading them sequentially. But if you need to read a key randomly (by not using it's iterator), it is much slower than Sqlite.

Raw Sqlite is fast for random reading if you use it correctly. (Use indexes, cover indexes, etc.) But you will lose the ability to read and write arbitrary key/value.

FMDB gives you a nice Objective-C API but 50% CPU time is wasted when converting data between Objective C and C (NSString <-> char*) (It's still a lot faster than Core Data, 4X+)

Personally I think Core Data sucks. So I won't recommend it to anyone for any usage. Sorry I don't want to recall those pain when using CoreData.

NSUserDefaults is best for user setting and only for user setting, definitely not a solution for long articles.

I use raw Sqlite and Google Protocol Buffer.

Protocol Buffer is extremely fast for encoding and decoding. It also gives you the ability to add new fields by modifying it's .proto file, no DB scheme change is required. You can store several related keys in a single proto, let's say articleMetaData.proto contains the title, author and publishDate, if some time later you need to add modifyDate you just add it to the proto file and store the binary in Sqlite in the same column.

Upvotes: 9

Rafael Nobre
Rafael Nobre

Reputation: 5131

A simple SQLite schema such as ID (indexed) | key | value should give you the freedom you want, with the speed and query capabilities you need. You can use a thin wrapper like FMDB to make it much easier to deal than the C SQLite API.

Upvotes: 4

Duncan Groenewald
Duncan Groenewald

Reputation: 8988

Core Data sounds like what you need, not sure how many records you are planning on storing, if it's just one then maybe not Core Data but if it's lots then I would think Core Data would be ideal.

You may need to explain what you mean by storing arbitrary keys/values, based on your question it sounds more like structured data (title, date, text and other meta data fields), in which case Core Data would be perfect.

Upvotes: 2

JonahGabriel
JonahGabriel

Reputation: 3094

Personally, rather than using NSUserDefaults which is fine for simple data, I would look into using the NSCoding protocol and NSKeyedArchiver. Have your objects implement the NSCoding protocol and then use something like this to archive/unarchive them:

+ (NSObject *)readArchiveFile:(NSString *)inFileName
{
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectoryPath, inFileName];

    NSObject *returnObject = nil;
    if( [fileMgr fileExistsAtPath:filePath] )
    {

        returnObject = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    }

    return returnObject;

}

+ (void)archiveFile:(NSString *)inFileName inObject:(NSObject *)inObject
{
    NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectoryPath, inFileName];
    BOOL didSucceed = [NSKeyedArchiver archiveRootObject:inObject toFile:filePath];
    if( !didSucceed )
    {
        NSLog(@"File %@ write operation %@", inFileName, didSucceed ? @"success" : @"error" );
    }

}

+ (void)deleteFile:(NSString *)inFileName
{
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSString *documentsDirectoryPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsDirectoryPath, inFileName];
    NSError *error;
    if ( [fileMgr fileExistsAtPath:filePath] && [fileMgr removeItemAtPath:filePath error:&error] != YES)
    {
        NSLog(@"Unable to delete file: %@", [error localizedDescription]);
    }
}

Also, I would look into using CoreData as it gives you the ability to query your data.

Upvotes: 2

Miguel Chaves
Miguel Chaves

Reputation: 139

You have NSUserDefaults and there you can store information.

Have only a few things to retain: 1 - If you do not leave the app is necessary to do synchronize to store the information; 2 - You can store and get and edit you information always

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.emailToSend forKey:@"teste.email"];
[defaults synchronize];


NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[alert textFieldAtIndex:0].text = [defaults objectForKey:@"teste.email"];

Upvotes: 1

Stephen Johnson
Stephen Johnson

Reputation: 5121

Look at NSUserDefaults.

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

Upvotes: 1

Related Questions