Top of the page
Close

This is a red error

This is a red error

This is a red error

Small database handling library optimised for UITableView

Ondrej Rafaj on 2010.02.23 15:34:21

Attached files
xProgress - wgFileDb.docx Download
wgfiledb-source-libraries.zip Download

Hey Kids, today I want to tell you about library I wrote. It works like a tiny little database (without search), it is reusable within the application as all entries are stored in their own file. The entire library is based on NSArray (table) and NSDictionary (row).

This library should cover all the main functions everyone needs when working with tables, including easy reordering system. This library is not designed for large amount of data, but up to 100 items should be fine. Sorry for calling this thing a db but I’ve had no better idea ...

Ok, lets start with the functions that are available in the library. Two files that are included in the header file are included in the archive, which can be downloaded in the top right corner of this tutorial. I will describe what each function is doing later.

//
//  wgFileDb.h
//  iBBClone
//
//  Created by Ondrej Rafaj on 21.2.10.
//  Copyright 2010 Home. All rights reserved.
//

#import 
#import "wgIo.h"
#import "wgPaths.h"




#define kFileDbFolder		@"wgFileDb"
#define kFileDbAIKey		@"autoincrement"
#define kFileDbDataKey		@"data"
#define kFileDbIndexKey		@"index"
#define kMainIdKey			@"wgFileDbSecretId"



@interface wgFileDb : NSObject {

}

+ (int)getId:(NSDictionary *)item;

+ (NSString *)getPathToDb:(NSString *)dbName;

+ (BOOL)isDatabase:(NSString *)dbName;

+ (BOOL)deleteDb:(NSString *)dbName;

+ (BOOL)truncateDb:(NSString *)dbName;

+ (int)getAutoincrementNumberForDb:(NSString *)dbName;

+ (NSArray *)getItemsFromDb:(NSString *)dbName;

+ (void)saveFullData:(NSArray *)arr toDb:(NSString *)dbName;

+ (int)getNumberOfItemsInDb:(NSString *)dbName;

+ (NSDictionary *)getItem:(int)idItem inDb:(NSString *)dbName;

+ (int)addItemToBottom:(NSDictionary *)item intoDb:(NSString *)dbName;

+ (int)addItemToTop:(NSDictionary *)item intoDb:(NSString *)dbName;

+ (void)deleteItem:(int)idItem fromDb:(NSString *)dbName;

+ (void)updateItem:(int)idItem withData:(NSDictionary *)item inDb:(NSString *)dbName;

+ (void)moveTableItem:(NSIndexPath *)fromIndexPath to:(NSIndexPath *)toIndexPath inDb:(NSString *)dbName;

+ (int)duplicateItemToBottom:(int)idItem inDb:(NSString *)dbName;

+ (int)duplicateItemToTop:(int)idItem inDb:(NSString *)dbName;

@end

As you can see from the list, there is quite few methods that are giving you an opportunity to manage almost any dataset. The only thing I want to add is sorting.

Here you can see the .m file with all the comments that are relevant to each function.

//
//  wgFileDb.m
//  iBBClone
//
//  Created by Ondrej Rafaj on 21.2.10.
//  Copyright 2010 Home. All rights reserved.
//

#import "wgFileDb.h"

@implementation wgFileDb

// public; returns id from given object
+ (int)getId:(NSDictionary *)item {
	return [[item objectForKey:kMainIdKey] intValue];
}

// private; converts number to number in the string
+ (NSString *)_encodeNumber:(int)num {
	return [NSString stringWithFormat:@"%d", num];
}

// private; returns path to the documents/db folder
+ (NSString *)_getPathToDb:(NSString *)dbName {
	NSString *p = [NSString stringWithFormat:@"%@%@/", [wgPaths databaseDirectory], kFileDbFolder];
	[wgIo createFolderIfNotExists:p];
	return [NSString stringWithFormat:@"%@%@", p, dbName];
}

// public; returns path to the documents/db folder, creates the path if this one doesn't exist and creates empty db file
+ (NSString *)getPathToDb:(NSString *)dbName {
	NSString *p = [self _getPathToDb:dbName];
	if (![wgIo isFile:p]) {
		NSMutableDictionary *d = [[[NSMutableDictionary alloc] init] autorelease];
		[d setObject:[self _encodeNumber:0] forKey:kFileDbAIKey];
		[d setObject:[[[NSMutableArray alloc] init] autorelease] forKey:kFileDbDataKey];
		[d setObject:[[[NSMutableDictionary alloc] init] autorelease] forKey:kFileDbIndexKey];
		[d writeToFile:p atomically:YES];
		if (![wgIo isFile:p]) NSAssert(@"Unable to create database '%@' in wgFileDb.", dbName);
	}
	return p;
}

// public; checks if database file exists
+ (BOOL)isDatabase:(NSString *)dbName {
	NSString *p = [self _getPathToDb:dbName];
	return [wgIo isFile:p];
}

// private; returns full database file
+ (NSDictionary *)_getFullDataForDb:(NSString *)dbName {
	NSString *p = [self getPathToDb:dbName];
	return [[[NSDictionary alloc] initWithContentsOfFile:p] autorelease];
}

// public; deletes the database file
+ (BOOL)deleteDb:(NSString *)dbName {
	NSString *p = [self getPathToDb:dbName];
	if ([wgIo isFile:p]) return [wgIo deleteFile:p];
	else return YES;
}

// public; truncates the databse fil and sets autoincrement value to zero
+ (BOOL)truncateDb:(NSString *)dbName {
	[self deleteDb:dbName];
	NSString *p = [self getPathToDb:dbName];
	return [wgIo isFile:p];
}

// public; returns value of actual autoincrement number
+ (int)getAutoincrementNumberForDb:(NSString *)dbName {
	NSDictionary *d = [self _getFullDataForDb:dbName];
	return (int) [[d objectForKey:kFileDbAIKey] intValue];
}

// public; returns all items from the db as NSArray
+ (NSArray *)getItemsFromDb:(NSString *)dbName {
	NSDictionary *d = [self _getFullDataForDb:dbName];
	return (NSArray *) [d objectForKey:kFileDbDataKey];
}

// private; converts id of the item to index position in the array
+ (int)_getIndexForId:(int)idItem inDb:(NSString *)dbName {
	NSDictionary *d = [self _getFullDataForDb:dbName];
	return (int) [[[d objectForKey:kFileDbIndexKey] objectForKey:[self _encodeNumber:idItem]] intValue];
}

// private; recreates index for all the id's and items in the db
+ (void)_reloadIndexTableInDb:(NSString *)dbName {
	NSMutableDictionary *f = (NSMutableDictionary *)[self _getFullDataForDb:dbName];
	NSArray *arr = (NSArray *)[f objectForKey:kFileDbDataKey];
	NSMutableDictionary *n = [[[NSMutableDictionary alloc] init] autorelease];
	int index = 0;
	for (NSDictionary *d in arr) {
		[n setObject:[self _encodeNumber:index] forKey:[d objectForKey:kMainIdKey]];
		index++;
	}
	[f setObject:n forKey:kFileDbIndexKey];
	NSString *p = [self getPathToDb:dbName];
	[f writeToFile:p atomically:YES];
}

// private; increases autoincrement value for the databse
+ (int)_increaseAIinDb:(NSString *)dbName {
	NSMutableDictionary *f = (NSMutableDictionary *)[self _getFullDataForDb:dbName];
	int ai = ([self getAutoincrementNumberForDb:dbName] + 1);
	[f setObject:[self _encodeNumber:ai] forKey:kFileDbAIKey];
	NSString *p = [self getPathToDb:dbName];
	[f writeToFile:p atomically:YES];
	return ai;
}

// public, saves the full given array back to he database
+ (void)saveFullData:(NSArray *)arr toDb:(NSString *)dbName {
	NSMutableDictionary *f = (NSMutableDictionary *)[self _getFullDataForDb:dbName];
	[f setObject:arr forKey:kFileDbDataKey];
	NSString *p = [self getPathToDb:dbName];
	[f writeToFile:p atomically:YES];
	[self _reloadIndexTableInDb:dbName];
}

// public; returns number of items in the selected db
+ (int)getNumberOfItemsInDb:(NSString *)dbName {
	NSArray *arr = [self getItemsFromDb:dbName];
	return [arr count];
}

// public; returns one item with specific id
+ (NSDictionary *)getItem:(int)idItem inDb:(NSString *)dbName {
	NSArray *arr = [self getItemsFromDb:dbName];
	return (NSDictionary *)[arr objectAtIndex:[self _getIndexForId:idItem inDb:dbName]];
}

// public; add given item to the end of the database
+ (int)addItemToBottom:(NSDictionary *)item intoDb:(NSString *)dbName {
	NSMutableArray *arr = (NSMutableArray *) [self getItemsFromDb:dbName];
	int i = [self _increaseAIinDb:dbName];
	NSMutableDictionary *nd = (NSMutableDictionary *)item;
	[nd setObject:[self _encodeNumber:i] forKey:kMainIdKey];
	[arr addObject:nd];
	[self saveFullData:arr toDb:dbName];
	return i;
}

// public; add given item to the beginning of the database
+ (int)addItemToTop:(NSDictionary *)item intoDb:(NSString *)dbName {
	NSMutableArray *arr = (NSMutableArray *) [self getItemsFromDb:dbName];
	NSMutableArray *narr = [[[NSMutableArray alloc] init] autorelease];
	int i = [self _increaseAIinDb:dbName];
	NSMutableDictionary *nd = (NSMutableDictionary *)item;
	[nd setObject:[self _encodeNumber:i] forKey:kMainIdKey];
	[narr addObject:nd];
	for (NSDictionary *d in arr) [narr addObject:d];
	[self saveFullData:narr toDb:dbName];
	return i;
}

// public; deletes id specific item from the database
+ (void)deleteItem:(int)idItem fromDb:(NSString *)dbName {
	NSMutableArray *arr = (NSMutableArray *) [self getItemsFromDb:dbName];
	[arr removeObjectAtIndex:[self _getIndexForId:idItem inDb:dbName]];
	[self saveFullData:arr toDb:dbName];
}

// public; updates id specific item in the database
+ (void)updateItem:(int)idItem withData:(NSDictionary *)item inDb:(NSString *)dbName {
	NSMutableArray *arr = (NSMutableArray *) [self getItemsFromDb:dbName];
	NSMutableArray *narr = [[[NSMutableArray alloc] init] autorelease];
	int ai;
	for (NSDictionary *d in arr) {
		ai = [[d objectForKey:kMainIdKey] intValue];
		if (ai == idItem) [narr addObject:item];
		else [narr addObject:d];
	}
	[self saveFullData:narr toDb:dbName];
}

// public; moves item in the db, the input values are the same that are coming from:
// - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
+ (void)moveTableItem:(NSIndexPath *)fromIndexPath to:(NSIndexPath *)toIndexPath inDb:(NSString *)dbName {
	NSMutableArray *arr = (NSMutableArray *) [self getItemsFromDb:dbName];
	NSDictionary *i = [[arr objectAtIndex:fromIndexPath.row] retain];
	[arr removeObjectAtIndex:fromIndexPath.row];
	[arr insertObject:i atIndex:toIndexPath.row];
	[self saveFullData:arr toDb:dbName];
}

// public; duplicates id specific item in the database and adds this one to the end of db
+ (int)duplicateItemToBottom:(int)idItem inDb:(NSString *)dbName {
	NSMutableDictionary *d = (NSMutableDictionary *)[self getItem:idItem inDb:dbName];
	[d removeObjectForKey:kMainIdKey];
	return [self addItemToBottom:d intoDb:dbName];
}

// public; duplicates id specific item in the database and adds this one to the beginning of db
+ (int)duplicateItemToTop:(int)idItem inDb:(NSString *)dbName {
	NSMutableDictionary *d = (NSMutableDictionary *)[self getItem:idItem inDb:dbName];
	[d removeObjectForKey:kMainIdKey];
	return [self addItemToTop:d intoDb:dbName];
}

// method in development, comments welcome
+ (NSArray *)sortAscendingByKey:(NSString *)key inDb:(NSString *)dbName {
	return [self getItemsFromDb:dbName];
}

// method in development, comments welcome
+ (NSArray *)sortDescendingByKey:(NSString *)key inDb:(NSString *)dbName {
	return [self getItemsFromDb:dbName];
}

@end

In the end, there are the file handling libraries I’m using to unify paths and check for existing files.

File handling library:

//
//  wgIo.h
//  iDeviant
//
//  Created by Ondrej Rafaj on 26.11.09.
//  Copyright 2009 Home. All rights reserved.
//

#import 
#import "wgPaths.h"

@interface wgIo : NSObject {

}

+ (NSString *)readFile:(NSString *)filePath;

+ (NSDictionary *)getFileAttributes:(NSString *)path;

+ (NSMutableArray *)getFilesArray:(NSString *)path;

+ (int)getFileSize:(NSString *)path;

+ (NSString *)getFileExtension:(NSString *)path;

+ (NSString *)getFormatedFileSize:(NSString *)path;

+ (NSString *)getFileCreated:(NSString *)path;

+ (BOOL)createFolderIfNotExists:(NSString *)path;

+ (BOOL)isDirectory:(NSString *)path;

+ (BOOL)isFile:(NSString *)path;

+ (BOOL)deleteFile:(NSString *)path;


@end

Main library file:

//
//  wgIo.m
//  iDeviant
//
//  Created by Ondrej Rafaj on 26.11.09.
//  Copyright 2009 Home. All rights reserved.
//

#import "wgIo.h"


@implementation wgIo


+ (NSString *)readFile:(NSString *)filePath {
	NSError *error;
	return [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
}

+ (BOOL)isDirectory:(NSString *)path {
	BOOL isDir;
	NSFileManager *fileManager = [NSFileManager defaultManager];
	if ([fileManager fileExistsAtPath:path isDirectory:&isDir] && isDir) return YES;
	else return NO;
}

+ (BOOL)isFile:(NSString *)path {
	BOOL isDir;
	NSFileManager *fileManager = [NSFileManager defaultManager];
	if ([fileManager fileExistsAtPath:path isDirectory:&isDir]) {
		if (isDir) return NO;
		else return YES;
	}
	else return NO;
}

+ (BOOL)deleteFile:(NSString *)path {
	NSFileManager *fileManager = [NSFileManager defaultManager];
	return [fileManager removeItemAtPath:path error:nil];
}


+ (NSDictionary *)getFileAttributes:(NSString *)path {
	NSFileManager *manager = [NSFileManager defaultManager];
	NSDictionary *fileAttributes = [manager fileAttributesAtPath:path traverseLink:false];
	return fileAttributes;
}

+ (int)getFileSize:(NSString *)path {
	NSDictionary *fileAttributes = [self getFileAttributes:path];
	int ret = [[fileAttributes objectForKey:NSFileSize] intValue];
	return ret;
}

+ (NSString *)getFileExtension:(NSString *)path {
	NSString *ret = [path pathExtension];
	return ret;
}

+ (NSString *)getFileCreated:(NSString *)path {
	NSDictionary *fileAttributes = [self getFileAttributes:path];
	NSDate *fileCreationDate = [fileAttributes objectForKey:NSFileCreationDate];
	NSString *fileCreationDateString = [fileCreationDate description];
	return fileCreationDateString;
}

+ (NSString *)getFormatedFileSize:(NSString *)path {
	int fileSize = [self getFileSize:path];
	NSString *extension;
	NSString *formatedFileSize;
	float ff;
	if (fileSize < 1024) {
		formatedFileSize = [NSString stringWithFormat:@"%d", fileSize];
		extension = @"bytes";
	}
	else if (fileSize < 1048576) {
		ff = fileSize / 1024;
		formatedFileSize = [NSString stringWithFormat:@"%.2f", ff];
		extension = @"Kb";
	}
	else {
		ff = fileSize / 1048576;
		formatedFileSize = [NSString stringWithFormat:@"%.2f", ff];
		extension = @"Mb";
	}
	return [NSString stringWithFormat:@"%@ %@", formatedFileSize, extension];
}

+ (NSMutableArray *)getFilesArray:(NSString *)path {
	NSMutableArray *fileArray = [[[NSMutableArray alloc] init] autorelease];
	NSFileManager *manager = [NSFileManager defaultManager];
    NSArray *fileList = [manager directoryContentsAtPath:[wgPaths filesDirectory]];
    for (NSString *file in fileList){
        if (![file isEqualToString:@"~"]) [fileArray addObject:file];
	}
	return fileArray;
}

+ (BOOL)createFolderIfNotExists:(NSString *)path {
	if (![self isDirectory:path]) {
		NSFileManager *fileManager = [NSFileManager defaultManager];
		//NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: [NSDate date], NSFileModificationDate, @"owner", @"NSFileOwnerAccountName", @"group", @"NSFileGroupOwnerAccountName", nil, @"NSFilePosixPermissions", [NSNumber numberWithBool:YES], @"NSFileExtensionHidden", nil];
		return [fileManager createDirectoryAtPath:path attributes:nil];
	}
	else return YES;
}


@end

And here’s the library that handles paths.

Header file:

//
//  wgPaths.h
//  iDeviant
//
//  Created by Ondrej Rafaj on 26.11.09.
//  Copyright 2009 Home. All rights reserved.
//

#import 
#import "wgIo.h"

@interface wgPaths : NSObject {

}

+ (NSString *)tempDirectory;

+ (NSString *)confDirectory;

+ (NSString *)documentsDirectory;

+ (NSString *)filesDirectory;

+ (NSString *)webDirectory;

+ (NSString *)databaseDirectory;

+ (NSString *)systemDirectory;

+ (NSString *)cacheDirectory;

+ (NSString *)imageCacheDirectory;

+ (NSString *)imageCacheDirectory:(NSString *)subFolder;

+ (NSString *)sqlFile:(NSString *)databaseName;

@end

And at last the main lib file:

//
//  wgPaths.m
//  iDeviant
//
//  Created by Ondrej Rafaj on 26.11.09.
//  Copyright 2009 Home. All rights reserved.
//

#import "wgPaths.h"

#define kSysFolderName				@"~"

@implementation wgPaths

+ (NSString *)documentsDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
	[wgIo createFolderIfNotExists:[NSString stringWithFormat:@"%@/%@/", [paths objectAtIndex:0], kSysFolderName]];
	return [paths objectAtIndex:0];
}

+ (NSString *)confDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/%@/%@/", [self documentsDirectory], kSysFolderName, @"config"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)filesDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/%@/", [self documentsDirectory], @"files"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)tempDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/%@/%@/", [self documentsDirectory], kSysFolderName, @"temp"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)systemDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/%@/%@/", [self documentsDirectory], kSysFolderName, @"system"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)cacheDirectory {
	NSString *p = [NSString stringWithFormat:@"%@%@/", [wgPaths systemDirectory], @"cache"];
	if (![wgIo createFolderIfNotExists:p]) NSLog(@"Error creating directory: %@", p);
	return p;
}

+ (NSString *)imageCacheDirectory {
	NSString *p = [NSString stringWithFormat:@"%@%@/", [wgPaths cacheDirectory], @"imagecache"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)imageCacheDirectory:(NSString *)subFolder {
	NSString *p = [NSString stringWithFormat:@"%@%@/", [wgPaths imageCacheDirectory], subFolder];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)databaseDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/%@/%@/", [self documentsDirectory], kSysFolderName, @"db"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)webDirectory {
	NSString *p = [NSString stringWithFormat:@"%@/~/%@/", [self documentsDirectory], @"htdocs"];
	[wgIo createFolderIfNotExists:p];
	return p;
}

+ (NSString *)sqlFile:(NSString *)databaseName {
	NSString *p = [NSString stringWithFormat:@"%@%@.sqlite3", [self databaseDirectory], databaseName];
	[wgIo createFolderIfNotExists:p];
	return p;
}

@end

Thanks for reading, some tutorial about how to use those libraries with full functionality of UITableView (add, delete, reorder and edit) will follow shortly than stay tuned. :)

Have nice day,

Ondrej

Ondrej Rafaj

Ondrej Rafaj

Technical director @ Fuerte International

Back to top Comment

Comments ... Why not to get involved!
HTML Comment Box is loading comments...

Old comments:

Back to top

IE SUCKS, DON'T USE IT :) Digg this page