Authored by Joseph G

Added database caching of tiles

No preview for this file type
No preview for this file type
#import <Foundation/Foundation.h>
#import "sqlite3.h"
#import "FMResultSet.h"
@interface FMDatabase : NSObject
{
sqlite3* db;
NSString* databasePath;
BOOL logsErrors;
BOOL crashOnErrors;
BOOL inUse;
BOOL inTransaction;
BOOL traceExecution;
BOOL checkedOut;
int busyRetryTimeout;
}
+ (id)databaseWithPath:(NSString*)inPath;
- (id)initWithPath:(NSString*)inPath;
- (BOOL) open;
- (void) close;
- (BOOL) goodConnection;
// encryption methods. You need to have purchased the sqlite encryption extensions for these to work.
- (BOOL) setKey:(NSString*)key;
- (BOOL) rekey:(NSString*)key;
- (NSString *) databasePath;
- (NSString*) lastErrorMessage;
- (int) lastErrorCode;
- (BOOL) hadError;
- (sqlite_int64) lastInsertRowId;
- (sqlite3*) sqliteHandle;
- (BOOL) executeUpdate:(NSString *)sql arguments:(va_list)args;
- (BOOL) executeUpdate:(NSString*)sql, ...;
- (id) executeQuery:(NSString *)sql arguments:(va_list)args;
- (id) executeQuery:(NSString*)sql, ...;
- (BOOL) rollback;
- (BOOL) commit;
- (BOOL) beginTransaction;
- (BOOL) beginDeferredTransaction;
- (BOOL)logsErrors;
- (void)setLogsErrors:(BOOL)flag;
- (BOOL)crashOnErrors;
- (void)setCrashOnErrors:(BOOL)flag;
- (BOOL)inUse;
- (void)setInUse:(BOOL)flag;
- (BOOL)inTransaction;
- (void)setInTransaction:(BOOL)flag;
- (BOOL)traceExecution;
- (void)setTraceExecution:(BOOL)flag;
- (BOOL)checkedOut;
- (void)setCheckedOut:(BOOL)flag;
- (int)busyRetryTimeout;
- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout;
+ (NSString*) sqliteLibVersion;
@end
... ...
#import "FMDatabase.h"
@implementation FMDatabase
+ (id)databaseWithPath:(NSString*)aPath {
return [[[FMDatabase alloc] initWithPath:aPath] autorelease];
}
- (id)initWithPath:(NSString*)aPath {
self = [super init];
if (self) {
databasePath = [aPath copy];
db = 0x00;
logsErrors = 0x00;
crashOnErrors = 0x00;
busyRetryTimeout = 0x00;
}
return self;
}
- (void)dealloc {
[self close];
[databasePath release];
[super dealloc];
}
+ (NSString*) sqliteLibVersion {
return [NSString stringWithFormat:@"%s", sqlite3_libversion()];
}
- (NSString *) databasePath {
return databasePath;
}
- (sqlite3*) sqliteHandle {
return db;
}
- (BOOL) open {
int err = sqlite3_open( [databasePath fileSystemRepresentation], &db );
if(err != SQLITE_OK) {
NSLog(@"error opening!: %d", err);
return NO;
}
return YES;
}
- (void) close {
if (!db) {
return;
}
int rc;
BOOL retry;
int numberOfRetries = 0;
do {
retry = NO;
rc = sqlite3_close(db);
if (SQLITE_BUSY == rc) {
retry = YES;
usleep(20);
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
NSLog(@"Database busy, unable to close");
return;
}
}
else if (SQLITE_OK != rc) {
NSLog(@"error closing!: %d", rc);
}
}
while (retry);
db = nil;
}
- (BOOL) rekey:(NSString*)key {
#ifdef SQLITE_HAS_CODEC
if (!key) {
return NO;
}
int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String]));
if (rc != SQLITE_OK) {
NSLog(@"error on rekey: %d", rc);
NSLog(@"%@", [self lastErrorMessage]);
}
return (rc == SQLITE_OK);
#else
return NO;
#endif
}
- (BOOL) setKey:(NSString*)key {
#ifdef SQLITE_HAS_CODEC
if (!key) {
return NO;
}
int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String]));
return (rc == SQLITE_OK);
#else
return NO;
#endif
}
- (BOOL) goodConnection {
if (!db) {
return NO;
}
FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"];
if (rs) {
[rs close];
return YES;
}
return NO;
}
- (void) compainAboutInUse {
NSLog(@"The FMDatabase %@ is currently in use.", self);
if (crashOnErrors) {
*(long*)0 = 0xDEADBEEF;
}
}
- (NSString*) lastErrorMessage {
return [NSString stringWithUTF8String:sqlite3_errmsg(db)];
}
- (BOOL) hadError {
return ([self lastErrorCode] != SQLITE_OK);
}
- (int) lastErrorCode {
return sqlite3_errcode(db);
}
- (sqlite_int64) lastInsertRowId {
if (inUse) {
[self compainAboutInUse];
return NO;
}
[self setInUse:YES];
sqlite_int64 ret = sqlite3_last_insert_rowid(db);
[self setInUse:NO];
return ret;
}
- (void) bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; {
// FIXME - someday check the return codes on these binds.
if ([obj isKindOfClass:[NSData class]]) {
sqlite3_bind_blob(pStmt, idx, [obj bytes], [obj length], SQLITE_STATIC);
}
else if ([obj isKindOfClass:[NSDate class]]) {
sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]);
}
else if ([obj isKindOfClass:[NSNumber class]]) {
if (strcmp([obj objCType], @encode(BOOL)) == 0) {
sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0));
}
else if (strcmp([obj objCType], @encode(int)) == 0) {
sqlite3_bind_int64(pStmt, idx, [obj longValue]);
}
else if (strcmp([obj objCType], @encode(long)) == 0) {
sqlite3_bind_int64(pStmt, idx, [obj longValue]);
}
else if (strcmp([obj objCType], @encode(float)) == 0) {
sqlite3_bind_double(pStmt, idx, [obj floatValue]);
}
else if (strcmp([obj objCType], @encode(double)) == 0) {
sqlite3_bind_double(pStmt, idx, [obj doubleValue]);
}
else {
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
}
}
else {
sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC);
}
}
- (id) executeQuery:(NSString *)sql arguments:(va_list)args {
if (inUse) {
[self compainAboutInUse];
return nil;
}
[self setInUse:YES];
FMResultSet *rs = nil;
int rc;
sqlite3_stmt *pStmt;
if (traceExecution && sql) {
NSLog(@"%@ executeQuery: %@", self, sql);
}
int numberOfRetries = 0;
BOOL retry;
do {
retry = NO;
rc = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
if (SQLITE_BUSY == rc) {
retry = YES;
usleep(20);
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
NSLog(@"Database busy");
sqlite3_finalize(pStmt);
[self setInUse:NO];
return nil;
}
}
else if (SQLITE_OK != rc) {
rc = sqlite3_finalize(pStmt);
if (logsErrors) {
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
NSLog(@"DB Query: %@", sql);
if (crashOnErrors) {
#ifdef __BIG_ENDIAN__
asm{ trap };
#endif
*(long*)0 = 0xDEADBEEF;
}
}
[self setInUse:NO];
return nil;
}
}
while (retry);
id obj;
int idx = 0;
int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!)
while (idx < queryCount) {
obj = va_arg(args, id);
if (!obj) {
break;
}
if (traceExecution) {
NSLog(@"obj: %@", obj);
}
idx++;
[self bindObject:obj toColumn:idx inStatement:pStmt];
}
if (idx != queryCount) {
NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)");
sqlite3_finalize(pStmt);
[self setInUse:NO];
return nil;
}
// the statement gets close in rs's dealloc or [rs close];
rs = [FMResultSet resultSetWithStatement:pStmt usingParentDatabase:self];
[rs setQuery:sql];
return rs;
}
- (id) executeQuery:(NSString*)sql, ... {
va_list args;
va_start(args, sql);
id result = [self executeQuery:sql arguments:args];
va_end(args);
return result;
}
- (BOOL) executeUpdate:(NSString*)sql arguments:(va_list)args {
if (inUse) {
[self compainAboutInUse];
return NO;
}
[self setInUse:YES];
int rc = 0x00;
sqlite3_stmt *pStmt = 0x00;
if (traceExecution && sql) {
NSLog(@"%@ executeUpdate: %@", self, sql);
}
int numberOfRetries = 0;
BOOL retry;
do {
retry = NO;
rc = sqlite3_prepare(db, [sql UTF8String], -1, &pStmt, 0);
if (SQLITE_BUSY == rc) {
retry = YES;
usleep(20);
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
NSLog(@"Database busy");
sqlite3_finalize(pStmt);
[self setInUse:NO];
return NO;
}
}
else if (SQLITE_OK != rc) {
int ret = rc;
rc = sqlite3_finalize(pStmt);
if (logsErrors) {
NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]);
NSLog(@"DB Query: %@", sql);
if (crashOnErrors) {
#ifdef __BIG_ENDIAN__
asm{ trap };
#endif
*(long*)0 = 0xDEADBEEF;
}
}
[self setInUse:NO];
return ret;
}
}
while (retry);
id obj;
int idx = 0;
int queryCount = sqlite3_bind_parameter_count(pStmt);
while (idx < queryCount) {
obj = va_arg(args, id);
if (!obj) {
break;
}
if (traceExecution) {
NSLog(@"obj: %@", obj);
}
idx++;
[self bindObject:obj toColumn:idx inStatement:pStmt];
}
if (idx != queryCount) {
NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql);
sqlite3_finalize(pStmt);
[self setInUse:NO];
return NO;
}
/* Call sqlite3_step() to run the virtual machine. Since the SQL being
** executed is not a SELECT statement, we assume no data will be returned.
*/
numberOfRetries = 0;
do {
rc = sqlite3_step(pStmt);
retry = NO;
if (SQLITE_BUSY == rc) {
// this will happen if the db is locked, like if we are doing an update or insert.
// in that case, retry the step... and maybe wait just 10 milliseconds.
retry = YES;
usleep(20);
if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]);
NSLog(@"Database busy");
retry = NO;
}
}
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
// all is well, let's return.
}
else if (SQLITE_ERROR == rc) {
NSLog(@"Error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
NSLog(@"DB Query: %@", sql);
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
NSLog(@"Error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
NSLog(@"DB Query: %@", sql);
}
else {
// wtf?
NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db));
NSLog(@"DB Query: %@", sql);
}
} while (retry);
assert( rc!=SQLITE_ROW );
/* Finalize the virtual machine. This releases all memory and other
** resources allocated by the sqlite3_prepare() call above.
*/
rc = sqlite3_finalize(pStmt);
[self setInUse:NO];
return (rc == SQLITE_OK);
}
- (BOOL) executeUpdate:(NSString*)sql, ... {
va_list args;
va_start(args, sql);
BOOL result = [self executeUpdate:sql arguments:args];
va_end(args);
return result;
}
- (BOOL) rollback {
BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"];
if (b) {
inTransaction = NO;
}
return b;
}
- (BOOL) commit {
BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"];
if (b) {
inTransaction = NO;
}
return b;
}
- (BOOL) beginDeferredTransaction {
BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"];
if (b) {
inTransaction = YES;
}
return b;
}
- (BOOL) beginTransaction {
BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"];
if (b) {
inTransaction = YES;
}
return b;
}
- (BOOL)logsErrors {
return logsErrors;
}
- (void)setLogsErrors:(BOOL)flag {
logsErrors = flag;
}
- (BOOL)crashOnErrors {
return crashOnErrors;
}
- (void)setCrashOnErrors:(BOOL)flag {
crashOnErrors = flag;
}
- (BOOL)inUse {
return inUse || inTransaction;
}
- (void)setInUse:(BOOL)flag {
inUse = flag;
}
- (BOOL)inTransaction {
return inTransaction;
}
- (void)setInTransaction:(BOOL)flag {
inTransaction = flag;
}
- (BOOL)traceExecution {
return traceExecution;
}
- (void)setTraceExecution:(BOOL)flag {
traceExecution = flag;
}
- (BOOL)checkedOut {
return checkedOut;
}
- (void)setCheckedOut:(BOOL)flag {
checkedOut = flag;
}
- (int)busyRetryTimeout {
return busyRetryTimeout;
}
- (void)setBusyRetryTimeout:(int)newBusyRetryTimeout {
busyRetryTimeout = newBusyRetryTimeout;
}
@end
... ...
//
// FMDatabaseAdditions.h
// fmkit
//
// Created by August Mueller on 10/30/05.
// Copyright 2005 Flying Meat Inc.. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface FMDatabase (FMDatabaseAdditions)
- (int) intForQuery:(NSString*)objs, ...;
- (long) longForQuery:(NSString*)objs, ...;
- (BOOL) boolForQuery:(NSString*)objs, ...;
- (double) doubleForQuery:(NSString*)objs, ...;
- (NSData*) dataForQuery:(NSString*)objs, ...;
- (NSString*) stringForQuery:(NSString*)objs, ...;
@end
... ...
//
// FMDatabaseAdditions.m
// fmkit
//
// Created by August Mueller on 10/30/05.
// Copyright 2005 Flying Meat Inc.. All rights reserved.
//
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
@implementation FMDatabase (FMDatabaseAdditions)
#define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \
va_list args; \
va_start(args, query); \
FMResultSet *resultSet = [self executeQuery:query arguments:args]; \
va_end(args); \
if (![resultSet next]) { return (type)0; } \
type ret = [resultSet sel:0]; \
[resultSet close]; \
[resultSet setParentDB:nil]; \
return ret;
- (NSString*)stringForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex);
}
- (int)intForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex);
}
- (long)longForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex);
}
- (BOOL)boolForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex);
}
- (double)doubleForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex);
}
- (NSData*)dataForQuery:(NSString*)query, ...; {
RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex);
}
@end
... ...
#import <Foundation/Foundation.h>
#import "sqlite3.h"
@class FMDatabase;
@interface FMResultSet : NSObject {
FMDatabase *parentDB;
sqlite3_stmt *pStmt;
//sqlite3 *db;
NSString *query;
NSMutableDictionary *columnNameToIndexMap;
BOOL columnNamesSetup;
}
+ (id) resultSetWithStatement:(sqlite3_stmt *)stmt usingParentDatabase:(FMDatabase*)aDB;
- (void) close;
- (NSString *)query;
- (void)setQuery:(NSString *)value;
- (void)setPStmt:(sqlite3_stmt *)newsqlite3_stmt;
- (void)setParentDB:(FMDatabase *)newDb;
- (NSUInteger) columnCount;
- (BOOL) next;
- (int) intForColumn:(NSString*)columnName;
- (int) intForColumnIndex:(int)columnIdx;
- (long) longForColumn:(NSString*)columnName;
- (long) longForColumnIndex:(int)columnIdx;
- (BOOL) boolForColumn:(NSString*)columnName;
- (BOOL) boolForColumnIndex:(int)columnIdx;
- (double) doubleForColumn:(NSString*)columnName;
- (double) doubleForColumnIndex:(int)columnIdx;
- (NSString*) stringForColumn:(NSString*)columnName;
- (NSString*) stringForColumnIndex:(int)columnIdx;
- (NSDate*) dateForColumn:(NSString*)columnName withFormatString:(NSString*)formatString;
- (NSDate*) dateForColumnIndex:(int)columnIdx withFormatString:(NSString*)formatString;
- (NSDate*) dateForColumn:(NSString*)columnName;
- (NSDate*) dateForColumnIndex:(int)columnIdx;
- (NSDate*) timeForColumn:(NSString*)columnName;
- (NSDate*) timeForColumnIndex:(int)columnIdx;
//- (NSDate*) timeForColumn:(NSString*)columnName sinceDate:(NSDate*)date;
//- (NSDate*) timeForColumnIndex:(int)columnIdx sinceDate:(NSDate*)dateOffset;
- (NSData*) dataForColumn:(NSString*)columnName;
- (NSData*) dataForColumnIndex:(int)columnIdx;
- (void) kvcMagic:(id)object;
@end
... ...
#import "FMResultSet.h"
#import "FMDatabase.h"
@interface FMResultSet (Private)
- (NSMutableDictionary *)columnNameToIndexMap;
- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value;
@end
static NSDateFormatter *dateFormatter = nil;
static NSDateFormatter *timeFormatter = nil;
@implementation FMResultSet
+ (id) resultSetWithStatement:(sqlite3_stmt *)stmt usingParentDatabase:(FMDatabase*)aDB {
FMResultSet *rs = [[FMResultSet alloc] init];
[rs setPStmt:stmt];
[rs setParentDB:aDB];
return [rs autorelease];
}
- (id)init {
self = [super init];
if (self) {
[self setColumnNameToIndexMap:[NSMutableDictionary dictionary]];
}
return self;
}
- (void)dealloc {
[self close];
[query release];
query = nil;
[columnNameToIndexMap release];
columnNameToIndexMap = nil;
[super dealloc];
}
- (void) close {
[parentDB setInUse:NO];
if (!pStmt) {
return;
}
/* Finalize the virtual machine. This releases all memory and other
** resources allocated by the sqlite3_prepare() call above.
*/
int rc = sqlite3_finalize(pStmt);
if (rc != SQLITE_OK) {
NSLog(@"error finalizing for query: %@", [self query]);
}
pStmt = nil;
}
- (void) setupColumnNames {
int columnCount = sqlite3_column_count(pStmt);
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
[columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx]
forKey:[[NSString stringWithUTF8String:sqlite3_column_name(pStmt, columnIdx)] lowercaseString]];
}
columnNamesSetup = YES;
}
- (void) kvcMagic:(id)object {
int columnCount = sqlite3_column_count(pStmt);
int columnIdx = 0;
for (columnIdx = 0; columnIdx < columnCount; columnIdx++) {
const char *c = (const char *)sqlite3_column_text(pStmt, columnIdx);
// check for a null row
if (c) {
NSString *s = [NSString stringWithUTF8String:c];
[object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name(pStmt, columnIdx)]];
}
}
}
- (NSUInteger) columnCount
{
return sqlite3_column_count(pStmt);
}
- (BOOL) next {
int rc;
BOOL retry;
int numberOfRetries = 0;
do {
retry = NO;
rc = sqlite3_step(pStmt);
if (SQLITE_BUSY == rc) {
// this will happen if the db is locked, like if we are doing an update or insert.
// in that case, retry the step... and maybe wait just 10 milliseconds.
retry = YES;
usleep(20);
if ([parentDB busyRetryTimeout] && (numberOfRetries++ > [parentDB busyRetryTimeout])) {
NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [parentDB databasePath]);
NSLog(@"Database busy");
break;
}
}
else if (SQLITE_DONE == rc || SQLITE_ROW == rc) {
// all is well, let's return.
}
else if (SQLITE_ERROR == rc) {
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
break;
}
else if (SQLITE_MISUSE == rc) {
// uh oh.
NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
break;
}
else {
// wtf?
NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle]));
break;
}
} while (retry);
if (rc != SQLITE_ROW) {
[self close];
}
return (rc == SQLITE_ROW);
}
- (int) columnIndexForName:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
columnName = [columnName lowercaseString];
NSNumber *n = [columnNameToIndexMap objectForKey:columnName];
if (n) {
return [n intValue];
}
NSLog(@"Warning: I could not find the column named '%@'.", columnName);
return -1;
}
- (int) intForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return 0;
}
return sqlite3_column_int(pStmt, columnIdx);
}
- (int) intForColumnIndex:(int)columnIdx {
return sqlite3_column_int(pStmt, columnIdx);
}
- (long) longForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return 0;
}
return sqlite3_column_int64(pStmt, columnIdx);
}
- (long) longForColumnIndex:(int)columnIdx {
return sqlite3_column_int64(pStmt, columnIdx);
}
- (BOOL) boolForColumn:(NSString*)columnName {
return ([self intForColumn:columnName] != 0);
}
- (BOOL) boolForColumnIndex:(int)columnIdx {
return ([self intForColumnIndex:columnIdx] != 0);
}
- (double) doubleForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return 0;
}
return sqlite3_column_double(pStmt, columnIdx);
}
- (double) doubleForColumnIndex:(int)columnIdx {
return sqlite3_column_double(pStmt, columnIdx);
}
#pragma mark string functions
- (NSString*) stringForColumnIndex:(int)columnIdx {
const char *c = (const char *)sqlite3_column_text(pStmt, columnIdx);
if (!c) {
// null row.
return nil;
}
return [NSString stringWithUTF8String:c];
}
- (NSString*) stringForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return nil;
}
return [self stringForColumnIndex:columnIdx];
}
- (NSDate*) dateForColumn:(NSString*)columnName withFormatString:(NSString*)formatString
{
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return nil;
}
return [self dateForColumnIndex:columnIdx withFormatString:formatString];
}
- (NSDate*) dateForColumnIndex:(int)columnIdx withFormatter:(NSDateFormatter*)formatter
{
NSString *str = [self stringForColumnIndex:columnIdx];
if (str == nil)
return nil;
// [formatter setGeneratesCalendarDates:YES];
NSDate *d = [formatter dateFromString:str];
return d;
}
- (NSDate*) dateForColumnIndex:(int)columnIdx withFormatString:(NSString*)formatString
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:formatString allowNaturalLanguage:NO];
NSDate *d = [self dateForColumnIndex:columnIdx withFormatter:formatter];
[formatter release];
return d;
}
- (NSDate*) dateForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return nil;
}
return [self dateForColumnIndex:columnIdx];
}
// This function has not been tested.
- (NSDate*) dateForColumnIndex:(int)columnIdx {
if (dateFormatter == nil)
{
dateFormatter = [[NSDateFormatter alloc] initWithDateFormat:@"%Y-%m-%d" allowNaturalLanguage:NO];
}
return [self dateForColumnIndex:columnIdx withFormatter:dateFormatter];
}
- (NSDate*) timeForColumn:(NSString*)columnName
{
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return nil;
}
return [self timeForColumnIndex:columnIdx];
}
- (NSDate*) timeForColumnIndex:(int)columnIdx
{
if (timeFormatter == nil)
{
timeFormatter = [[NSDateFormatter alloc] initWithDateFormat:@"%H:%M:%S" allowNaturalLanguage:NO];
}
return [self dateForColumnIndex:columnIdx withFormatter:timeFormatter];
}
- (NSData*) dataForColumn:(NSString*)columnName {
if (!columnNamesSetup) {
[self setupColumnNames];
}
int columnIdx = [self columnIndexForName:columnName];
if (columnIdx == -1) {
return nil;
}
int dataSize = sqlite3_column_bytes(pStmt, columnIdx);
NSMutableData *data = [NSMutableData dataWithLength:dataSize];
memcpy([data mutableBytes], sqlite3_column_blob(pStmt, columnIdx), dataSize);
return data;
}
- (NSData*) dataForColumnIndex:(int)columnIdx {
int dataSize = sqlite3_column_bytes(pStmt, columnIdx);
NSMutableData *data = [NSMutableData dataWithLength:dataSize];
memcpy([data mutableBytes], sqlite3_column_blob(pStmt, columnIdx), dataSize);
return data;
}
- (void)setPStmt:(sqlite3_stmt *)newsqlite3_stmt {
pStmt = newsqlite3_stmt;
}
- (void)setParentDB:(FMDatabase *)newDb {
parentDB = newDb;
}
- (NSString *)query {
return query;
}
- (void)setQuery:(NSString *)value {
[value retain];
[query release];
query = value;
}
- (NSMutableDictionary *)columnNameToIndexMap {
return columnNameToIndexMap;
}
- (void)setColumnNameToIndexMap:(NSMutableDictionary *)value {
[value retain];
[columnNameToIndexMap release];
columnNameToIndexMap = value;
}
@end
... ...
This is FMDB; an objective-c wrapper around SQLITE by August Mueller from here:
http://gusmueller.com/blog/archives/2008/03/fmdb_for_iphone.html
\ No newline at end of file
... ...
#import <Foundation/Foundation.h>
#import "FMDatabase.h"
#import "FMDatabaseAdditions.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// delete the old db.
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeFileAtPath:@"/tmp/tmp.db" handler:nil];
FMDatabase* db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
if (![db open]) {
NSLog(@"Could not open db.");
return 0;
}
// create a bad statement, just to test the error code.
[db executeUpdate:@"blah blah blah"];
if ([db hadError]) {
NSLog(@"Err %d: %@", [db lastErrorCode], [db lastErrorMessage]);
}
// but of course, I don't bother checking the error codes below.
// Bad programmer, no cookie.
[db executeUpdate:@"create table test (a text, b text, c integer, d double, e double)"];
[db beginTransaction];
int i = 0;
while (i++ < 20) {
[db executeUpdate:@"insert into test (a, b, c, d, e) values (?, ?, ?, ?, ?)" ,
@"hi'", // look! I put in a ', and I'm not escaping it!
[NSString stringWithFormat:@"number %d", i],
[NSNumber numberWithInt:i],
[NSDate date],
[NSNumber numberWithFloat:2.2f]];
}
[db commit];
FMResultSet *rs = [db executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
while ([rs next]) {
// just print out what we've got in a number of formats.
NSLog(@"%d %@ %@ %@ %@ %f %f",
[rs intForColumn:@"c"],
[rs stringForColumn:@"b"],
[rs stringForColumn:@"a"],
[rs stringForColumn:@"rowid"],
[rs dateForColumn:@"d"],
[rs doubleForColumn:@"d"],
[rs doubleForColumn:@"e"]);
}
// close the result set.
// it'll also close when it's dealloc'd, but we're closing the database before
// the autorelease pool closes, so sqlite will complain about it.
[rs close];
// ----------------------------------------------------------------------------------------
// blob support.
[db executeUpdate:@"create table blobTable (a text, b blob)"];
// let's read in an image from safari's app bundle.
NSData *d = [NSData dataWithContentsOfFile:@"/Applications/Safari.app/Contents/Resources/compass.icns"];
if (d) {
[db executeUpdate:@"insert into blobTable (a, b) values (?,?)", @"safari's compass", d];
rs = [db executeQuery:@"select b from blobTable where a = ?", @"safari's compass"];
if ([rs next]) {
d = [rs dataForColumn:@"b"];
[d writeToFile:@"/tmp/compass.icns" atomically:NO];
// let's look at our fancy image that we just wrote out..
system("/usr/bin/open /tmp/compass.icns");
}
else {
NSLog(@"Could not select image.");
}
[rs close];
}
else {
NSLog(@"Can't find compass image..");
}
// test out the convenience methods in +Additions
[db executeUpdate:@"create table t1 (a integer)"];
[db executeUpdate:@"insert into t1 values (?)", [NSNumber numberWithUnsignedInteger:5]];
int a = [db intForQuery:@"select a from t1 where a = ?", [NSNumber numberWithUnsignedInteger:5]];
if (a != 5) {
NSLog(@"intForQuery didn't work (a != 5)");
}
// test the busy rety timeout schtuff.
[db setBusyRetryTimeout:50000];
FMDatabase *newDb = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
[newDb open];
rs = [newDb executeQuery:@"select rowid,* from test where a = ?", @"hi'"];
[rs next]; // just grab one... which will keep the db locked.
NSLog(@"Testing the busy timeout");
BOOL success = [db executeUpdate:@"insert into t1 values (5)"];
if (success) {
NSLog(@"Whoa- the database didn't stay locked!");
return 7;
}
else {
NSLog(@"Hurray, our timeout worked");
}
[rs close];
[newDb close];
success = [db executeUpdate:@"insert into t1 values (5)"];
if (!success) {
NSLog(@"Whoa- the database shouldn't be locked!");
return 8;
}
else {
NSLog(@"Hurray, we can insert again!");
}
[db close];
[pool release];
return 0;
}
... ...
//
// RMDatabaseCache.h
// RouteMe
//
// Created by Joseph Gentle on 19/09/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "RMTileCache.h"
@interface RMDatabaseCache : NSObject<RMTileCache> {
}
// +(void) install;
@end
... ...
//
// RMDatabaseCache.m
// RouteMe
//
// Created by Joseph Gentle on 19/09/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "RMDatabaseCache.h"
#import "RMTileCacheDAO.h"
#import "RMTileImage.h"
#import "RMTile.h"
//static BOOL installed = NO;
@implementation RMDatabaseCache
-(id) init
{
if (![super init])
return nil;
// NSLog(@"%d items in DB", [[DAO sharedManager] count]);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(addImageData:)
name:RMMapImageLoadedNotification
object:nil];
return self;
}
-(void) dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
-(void) addImageData: (NSNotification *)notification
{
// NSLog(@"AddImageData");
NSData *data = [[notification userInfo] objectForKey:@"data"];
RMTileImage *image = (RMTileImage*)[notification object];
[[RMTileCacheDAO sharedManager] addData:data LastUsed:[image lastUsedTime] ForTile:RMTileHash([image tile])];
// NSLog(@"%d items in DB", [[DAO sharedManager] count]);
}
-(RMTileImage*) cachedImage:(RMTile)tile
{
// NSLog(@"Looking for cached image in DB");
NSData *data = [[RMTileCacheDAO sharedManager] dataForTile:RMTileHash(tile)];
if (data == nil)
return nil;
RMTileImage *image = [RMTileImage imageWithTile:tile FromData:data];
// NSLog(@"DB cache hit for tile %d %d %d", tile.x, tile.y, tile.zoom);
return image;
}
/*
+(void) install
{
if (installed)
return;
RMDatabaseCache *dbCache = [[RMDatabaseCache alloc] init];
[[RMTileCache sharedCache] addCache:dbCache];
[dbCache release];
installed = YES;
}*/
@end
... ...
... ... @@ -10,6 +10,7 @@
#import "RMMemoryCache.h"
#import "RMDiskCache.h"
#import "RMDatabaseCache.h"
static RMTileCache *cache = nil;
... ... @@ -20,16 +21,19 @@ static RMTileCache *cache = nil;
if (![super init])
return nil;
caches = [[NSMutableArray alloc] init];
RMMemoryCache *memoryCache = [[RMMemoryCache alloc] init];
RMDiskCache *diskCache = [[RMDiskCache alloc] init];
caches = [[NSMutableArray alloc] init];
RMDatabaseCache *dbCache = [[RMDatabaseCache alloc] init];
[self addCache:memoryCache];
[self addCache:diskCache];
[self addCache:dbCache];
[memoryCache release];
[diskCache release];
[dbCache release];
return self;
}
... ...
//
// DAO.h
// CatchMe
//
// Created by Joseph Gentle on 21/07/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
@class FMDatabase;
@interface RMTileCacheDAO : NSObject {
FMDatabase* db;
}
+(RMTileCacheDAO*) sharedManager;
-(NSUInteger) count;
-(NSData*) dataForTile: (uint64_t) tileHash;
-(void) touchTile: (uint64_t) tileHash;
-(void) addData: (NSData*) data LastUsed: (NSDate*)date ForTile: (uint64_t) tileHash;
-(void) removeOldestFromDatabase;
//-(NSArray*) getLocalTimetableInformationAt: (int)stopId;
@end
... ...
//
// DAO.m
// CatchMe
//
// Created by Joseph Gentle on 21/07/08.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "RMTileCacheDAO.h"
#import "FMDatabase.h"
#import "RMTileCache.h"
#import "RMTileImage.h"
static RMTileCacheDAO *sharedDAOManager = nil;
@implementation RMTileCacheDAO
-(void)configureDBForFirstUse
{
// [db executeUpdate:@"DROP TABLE ZCACHE"];
[db executeUpdate:@"CREATE TABLE IF NOT EXISTS ZCACHE (ztileHash INTEGER PRIMARY KEY, zlastUsed DATE, zdata BLOB)"];
}
- (NSString*)dbPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] > 0)
{
// only copying one file
return [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Map.sqlite"];
}
return nil;
}
- (id)init
{
if (![super init])
return nil;
NSString *path = [self dbPath];
NSLog(@"Opening database at %@", path);
db = [[FMDatabase alloc] initWithPath:path];
if (![db open])
{
NSLog(@"Could not connect to database - %@", [db lastErrorMessage]);
// return nil;
}
[db setCrashOnErrors:TRUE];
[self configureDBForFirstUse];
return self;
}
- (void)dealloc
{
[db release];
[super dealloc];
}
-(NSUInteger) count
{
FMResultSet *results = [db executeQuery:@"SELECT COUNT(ztileHash) FROM ZCACHE"];
int count = 0;
if ([results next])
count = [results intForColumnIndex:0];
else
{
NSLog(@"Unable to count columns");
}
[results close];
return count;
}
-(NSData*) dataForTile: (uint64_t) tileHash
{
FMResultSet *results = [db executeQuery:@"SELECT zdata FROM ZCACHE WHERE ztilehash = ?", [NSNumber numberWithUnsignedLongLong:tileHash]];
if ([db hadError])
{
NSLog(@"DB error while fetching tile data: %@", [db lastErrorMessage]);
return nil;
}
NSData *data = nil;
if ([results next])
{
data = [results dataForColumnIndex:0];
}
[results close];
return data;
}
-(void) removeOldestFromDatabase
{
}
-(void) touchTile: (uint64_t) tileHash
{
}
-(void) addData: (NSData*) data LastUsed: (NSDate*)date ForTile: (uint64_t) tileHash
{
// Fixme
// NSLog(@"addData\t%d", tileHash);
BOOL result = [db executeUpdate:@"INSERT OR IGNORE INTO ZCACHE (ztileHash, zlastUsed, zdata) VALUES (?, ?, ?)", [NSNumber numberWithUnsignedLongLong:tileHash], date, data];
if (result == NO)
{
NSLog(@"Error occured adding data");
}
// NSLog(@"done\t%d", tileHash);
}
// Singleton gunk as per CocoaFundamentals page 99.
+ (RMTileCacheDAO*)sharedManager
{
@synchronized(self) {
if (sharedDAOManager == nil) {
[[self alloc] init]; // assignment not done here
}
}
return sharedDAOManager;
}
+ (id)allocWithZone:(NSZone *)zone
{
@synchronized(self) {
if (sharedDAOManager == nil) {
sharedDAOManager = [super allocWithZone:zone];
return sharedDAOManager; // assignment and return on first allocation
}
}
return nil; //on subsequent allocation attempts return nil
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return UINT_MAX; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
@end
... ...
... ... @@ -17,6 +17,20 @@
28D7ACF80DDB3853001CB0EB /* MapViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* MapViewViewController.m */; };
B83E66C30E80F053001663B6 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B83E65680E80E830001663B6 /* QuartzCore.framework */; };
B83E67170E80F1CE001663B6 /* loading.png in Resources */ = {isa = PBXBuildFile; fileRef = B83E670F0E80F1B5001663B6 /* loading.png */; };
B8474B9A0EB40094006A0BC1 /* FMDatabase.h in Headers */ = {isa = PBXBuildFile; fileRef = B8474B8D0EB40094006A0BC1 /* FMDatabase.h */; };
B8474B9B0EB40094006A0BC1 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B8E0EB40094006A0BC1 /* FMDatabase.m */; };
B8474B9C0EB40094006A0BC1 /* FMDatabaseAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = B8474B8F0EB40094006A0BC1 /* FMDatabaseAdditions.h */; };
B8474B9D0EB40094006A0BC1 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B900EB40094006A0BC1 /* FMDatabaseAdditions.m */; };
B8474B9E0EB40094006A0BC1 /* fmdb.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B910EB40094006A0BC1 /* fmdb.m */; };
B8474B9F0EB40094006A0BC1 /* FMResultSet.h in Headers */ = {isa = PBXBuildFile; fileRef = B8474B920EB40094006A0BC1 /* FMResultSet.h */; };
B8474BA00EB40094006A0BC1 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B930EB40094006A0BC1 /* FMResultSet.m */; };
B8474BA20EB40094006A0BC1 /* RMTileCacheDAO.h in Headers */ = {isa = PBXBuildFile; fileRef = B8474B960EB40094006A0BC1 /* RMTileCacheDAO.h */; };
B8474BA30EB40094006A0BC1 /* RMTileCacheDAO.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B970EB40094006A0BC1 /* RMTileCacheDAO.m */; };
B8474BA40EB40094006A0BC1 /* RMDatabaseCache.h in Headers */ = {isa = PBXBuildFile; fileRef = B8474B980EB40094006A0BC1 /* RMDatabaseCache.h */; };
B8474BA50EB40094006A0BC1 /* RMDatabaseCache.m in Sources */ = {isa = PBXBuildFile; fileRef = B8474B990EB40094006A0BC1 /* RMDatabaseCache.m */; };
B8474BC10EB4019A006A0BC1 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B8474BC00EB4019A006A0BC1 /* libsqlite3.dylib */; };
B8474BC30EB401AF006A0BC1 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = B8474BC00EB4019A006A0BC1 /* libsqlite3.dylib */; };
B8474BE80EB40404006A0BC1 /* README.txt in Resources */ = {isa = PBXBuildFile; fileRef = B8474BE70EB40404006A0BC1 /* README.txt */; };
B8C974170E8A19B2007D16AD /* RMMercatorToScreenProjection.h in Headers */ = {isa = PBXBuildFile; fileRef = B83E64C80E80E73F001663B6 /* RMMercatorToScreenProjection.h */; };
B8C974190E8A19B2007D16AD /* RMMapRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = B83E64CA0E80E73F001663B6 /* RMMapRenderer.h */; };
B8C9741D0E8A19B2007D16AD /* RMCoreAnimationRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = B83E64BD0E80E73F001663B6 /* RMCoreAnimationRenderer.h */; };
... ... @@ -169,6 +183,20 @@
B83E65680E80E830001663B6 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
B83E670F0E80F1B5001663B6 /* loading.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = loading.png; path = ApplicationSupport/loading.png; sourceTree = "<group>"; };
B83E673E0E80F332001663B6 /* MapView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MapView.app; sourceTree = BUILT_PRODUCTS_DIR; };
B8474B8D0EB40094006A0BC1 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabase.h; sourceTree = "<group>"; };
B8474B8E0EB40094006A0BC1 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabase.m; sourceTree = "<group>"; };
B8474B8F0EB40094006A0BC1 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMDatabaseAdditions.h; sourceTree = "<group>"; };
B8474B900EB40094006A0BC1 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMDatabaseAdditions.m; sourceTree = "<group>"; };
B8474B910EB40094006A0BC1 /* fmdb.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fmdb.m; sourceTree = "<group>"; };
B8474B920EB40094006A0BC1 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FMResultSet.h; sourceTree = "<group>"; };
B8474B930EB40094006A0BC1 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FMResultSet.m; sourceTree = "<group>"; };
B8474B950EB40094006A0BC1 /* Cache.xcdatamodel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = wrapper.xcdatamodel; path = Cache.xcdatamodel; sourceTree = "<group>"; };
B8474B960EB40094006A0BC1 /* RMTileCacheDAO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMTileCacheDAO.h; sourceTree = "<group>"; };
B8474B970EB40094006A0BC1 /* RMTileCacheDAO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMTileCacheDAO.m; sourceTree = "<group>"; };
B8474B980EB40094006A0BC1 /* RMDatabaseCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMDatabaseCache.h; sourceTree = "<group>"; };
B8474B990EB40094006A0BC1 /* RMDatabaseCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RMDatabaseCache.m; sourceTree = "<group>"; };
B8474BC00EB4019A006A0BC1 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
B8474BE70EB40404006A0BC1 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = "<group>"; };
B86F26AC0E87442C007A3773 /* RMMapLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RMMapLayer.h; sourceTree = "<group>"; };
B86F26E10E877802007A3773 /* DesktopMapView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DesktopMapView-Info.plist"; sourceTree = "<group>"; };
B8C9740E0E8A198F007D16AD /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.txt; sourceTree = "<group>"; };
... ... @@ -204,6 +232,7 @@
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */,
B83E66C30E80F053001663B6 /* QuartzCore.framework in Frameworks */,
B8C9787B0E8BE130007D16AD /* libMapView.a in Frameworks */,
B8474BC30EB401AF006A0BC1 /* libsqlite3.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -217,6 +246,7 @@
B8C974530E8A19B2007D16AD /* UIKit.framework in Frameworks */,
B8C974540E8A19B2007D16AD /* QuartzCore.framework in Frameworks */,
B8C974550E8A19B2007D16AD /* libProj4.a in Frameworks */,
B8474BC10EB4019A006A0BC1 /* libsqlite3.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -241,6 +271,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
B86F26E10E877802007A3773 /* DesktopMapView-Info.plist */,
B8474BC00EB4019A006A0BC1 /* libsqlite3.dylib */,
);
name = CustomTemplate;
sourceTree = "<group>";
... ... @@ -349,6 +380,7 @@
B83E64D50E80E73F001663B6 /* RMDiskCache.m */,
B8C974C80E8A9C30007D16AD /* RMCachedTileSource.h */,
B8C974C90E8A9C30007D16AD /* RMCachedTileSource.m */,
B8474B940EB40094006A0BC1 /* Database */,
);
name = Cache;
sourceTree = "<group>";
... ... @@ -400,6 +432,34 @@
name = Resources;
sourceTree = "<group>";
};
B8474B8C0EB40094006A0BC1 /* FMDB */ = {
isa = PBXGroup;
children = (
B8474B8D0EB40094006A0BC1 /* FMDatabase.h */,
B8474B8E0EB40094006A0BC1 /* FMDatabase.m */,
B8474B8F0EB40094006A0BC1 /* FMDatabaseAdditions.h */,
B8474B900EB40094006A0BC1 /* FMDatabaseAdditions.m */,
B8474B910EB40094006A0BC1 /* fmdb.m */,
B8474B920EB40094006A0BC1 /* FMResultSet.h */,
B8474B930EB40094006A0BC1 /* FMResultSet.m */,
B8474BE70EB40404006A0BC1 /* README.txt */,
);
path = FMDB;
sourceTree = "<group>";
};
B8474B940EB40094006A0BC1 /* Database */ = {
isa = PBXGroup;
children = (
B8474B8C0EB40094006A0BC1 /* FMDB */,
B8474B950EB40094006A0BC1 /* Cache.xcdatamodel */,
B8474B960EB40094006A0BC1 /* RMTileCacheDAO.h */,
B8474B970EB40094006A0BC1 /* RMTileCacheDAO.m */,
B8474B980EB40094006A0BC1 /* RMDatabaseCache.h */,
B8474B990EB40094006A0BC1 /* RMDatabaseCache.m */,
);
name = Database;
sourceTree = "<group>";
};
B86F26A80E8742ED007A3773 /* Layers */ = {
isa = PBXGroup;
children = (
... ... @@ -494,6 +554,11 @@
B8C974CA0E8A9C30007D16AD /* RMCachedTileSource.h in Headers */,
B8FA92190E9315EC003A9FE6 /* RMLayerSet.h in Headers */,
B8F3FC640EA2E792004D8F85 /* RMMarker.h in Headers */,
B8474B9A0EB40094006A0BC1 /* FMDatabase.h in Headers */,
B8474B9C0EB40094006A0BC1 /* FMDatabaseAdditions.h in Headers */,
B8474B9F0EB40094006A0BC1 /* FMResultSet.h in Headers */,
B8474BA20EB40094006A0BC1 /* RMTileCacheDAO.h in Headers */,
B8474BA40EB40094006A0BC1 /* RMDatabaseCache.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -578,6 +643,7 @@
28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */,
2899E5220DE3E06400AC0155 /* MapViewViewController.xib in Resources */,
B83E67170E80F1CE001663B6 /* loading.png in Resources */,
B8474BE80EB40404006A0BC1 /* README.txt in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ... @@ -626,6 +692,12 @@
B8FA921A0E9315EC003A9FE6 /* RMLayerSet.m in Sources */,
B8F3FC610EA2B382004D8F85 /* RMMapLayer.m in Sources */,
B8F3FC650EA2E792004D8F85 /* RMMarker.m in Sources */,
B8474B9B0EB40094006A0BC1 /* FMDatabase.m in Sources */,
B8474B9D0EB40094006A0BC1 /* FMDatabaseAdditions.m in Sources */,
B8474B9E0EB40094006A0BC1 /* fmdb.m in Sources */,
B8474BA00EB40094006A0BC1 /* FMResultSet.m in Sources */,
B8474BA30EB40094006A0BC1 /* RMTileCacheDAO.m in Sources */,
B8474BA50EB40094006A0BC1 /* RMDatabaseCache.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
... ...