2

I'm think there are some basic misunderstandings of how this should work and I would appreciate any help. I've been trying to write some functions that will pull data from a sqlite table, put it in a object, and save that object into an array. That array will then be accessed by other routines. There is a lot more to this class that I don't want to clog the forum with. The problems I'm having this with are:

1) I can only seem to access the final values that were put into the array. Am I saving the 'User' object incorrectly to the array?

2) Assuming that I get the objects correctly in the array, am I pulling them out correctly? I added a sample pull routine at the bottom.

3) Can anyone with more experience mention a more efficient way of doing this? I'm still trying to get my xcode programming legs under me and would appreciate any suggestions to writing cleaner code.

// **SQLiteFunctions header

#import <sqlite3.h>
#import "Users.h"

@interface SQLiteFunctions : NSObject
{
@public
    // initializing public variables
    NSMutableArray *returnData;

@private
    // initializing private variables
    sqlite3 *dbPointer;
    sqlite3_stmt *sqlStatement;
    NSArray *paths;
    NSString *documentsDirectory;
    NSString *databasePath;
    char *error;
    Users *users;
}

// initializing public member functions
-(BOOL)openDatabase: (NSString *) table_name;
-(BOOL)openBundleDatabase;
-(BOOL)openNonBundleDatabase;
-(void)closeDatabase;
-(BOOL)getRecords:(const char *)sqlquery;
-(int)getUserRecords:(const char *)sqlquery;

@end

//**SQLiteFunctions implementation

-(int)getUserRecords:(const char *)sqlquery
{
    // retreiving database values
    users = [[Users alloc] init];

    // initializing return data array
    returnData = [[NSMutableArray alloc] init];

    // testing prepare statement for verse sql
    if(sqlite3_prepare(dbPointer, sqlquery, -1, &sqlStatement, NULL) == SQLITE_OK)
    {
        // looping through existing return rows
        while (sqlite3_step(sqlStatement)==SQLITE_ROW)
        {
            // retreiving database values
            users->user_id = sqlite3_column_int(sqlStatement,0);
            users->user_name = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 1)];

            // adding verse object to the return data array
            [returnData addObject:users];
        }
    }
    else
    {
        // logging problem with prepare
        NSLog(@"Problem with prepare statement:  %s", sqlite3_errmsg(dbPointer));
        return NO;
    }

    // returning number of rows in the array
    return [returnData count];
}

//** Sample usage

// retreiving the number of users from the database
SQLiteFunctions *sql = [[SQLiteFunctions alloc] init];
Functions *func = [[Functions alloc] init];

// testing for successful open
if([sql openDatabase:@"users"])
{
    // testing for that profile name existing already
    if([sql getUserRecords:[func strCat:@"SELECT * FROM  users;":nil:nil:nil:nil]] > 0)
    {
        int user_id;
        NSString *user_name;
        for(Users *myUser in sql->returnData)
        {
            user_id = myUser.user_id;
            user_name = myUser.user_name;
            // do other stuff with values
        }
    }
}
Grymjack
  • 529
  • 1
  • 10
  • 21

1 Answers1

1

Problem adding objects to your array
The problem with your array is that you keep updating the values of the same instance of the User object, and adding it into the array over and over.

To fix this, move your users = [[Users alloc] init]; line inside your for loop.

Accessing objects in your array
Accessing the objects in the array is fine, if you need to go through all (or at least many) of the objects. If you only need a specific object, you can use:

Users *user5 = [sql->returnData objectAtIndex:5];

A bigger issue however, is that you really should not access ivar's from outside your own class. Instead of having a public ivar (returnData), you should create a declared property:

First, remove the ivar declaration line (NSMutableArray *returnData;), and then add this after the block that contains all of your ivar's (just above your // initializing public member functions comment):

@property (nonatomic, strong) NSMutableArray *returnData;

Then, you would access it using the accessor: sql.returnData or [sql returnData] instead of sql->returnData.

lnafziger
  • 25,760
  • 8
  • 60
  • 101
  • Thanks for the quick answer, I appreciate your help. Everything works well now. I took your advice on moving returnData to private. Now this is good for memory management? As I'm likely to be working on many apps that involve a significant amount of database management, I've been thinking about starting work on a more generic sqlite wrapper. The one I'm using in this app is more targeted for the specific tables. Is it worth it to develop a more complete API? Or just make targeted functions as needed? – Grymjack Dec 25 '12 at 07:08
  • 1
    Like most things in programming, it depends. You have to decide based on what you know, whether the extra time spent making something generic will save you at least that amount of time down the road on your other projects. Glad I could help out! As far as using a declared property instead of an ivar, take a look at this question: http://stackoverflow.com/q/4142177/937822 – lnafziger Dec 25 '12 at 17:00
  • Hmmmm....that makes things a little clearer *'[X complicatedThingHere]'*. :) As mentioned earlier, I am an Objective C beginner and I appreciate the time spent on helping my education along. – Grymjack Dec 25 '12 at 22:15
  • Anytime, that's why we are all here: to help each other out. Feel free to ask more questions, and please answer the questions on the subjects that you are more familiar with so that we can all accomplish our goal here! – lnafziger Dec 26 '12 at 01:40
  • Ok, I'll be a pest and take you up on the 'ask more questions' thing. :) I'd like to find a way of executing a code block when I exit from a seque. aka: a 'select profile' screen that has 'add new profile'. When the segue ends and returns to the 'select profile' view, I would like to update the table view to include the new profile. I've asked the questions different ways and have received no nibbles yet. http://stackoverflow.com/questions/14062532/xcode-additional-programming-upon-segue-exit and http://stackoverflow.com/questions/14030901/xcode-reloading-table-view-on-exiting-seque – Grymjack Dec 28 '12 at 18:33
  • No worries, I answered one of them for you. Hope it helps! – lnafziger Dec 28 '12 at 18:53