0

Novice Programmer - do this for fun and learning..

I am trying to populate 2 UIPickerView's with data from a SQLite query

Every time I run this, I get this error:

-[__NSArrayI length]: unrecognized selector sent to instance

What I'm trying to do is simple, and shouldn't be this painful a problem. Can you please review, and tell me what may be causing the error?

Please note, I have stripped a lot of the "fluffy" code out for the display elements - I am aware that none of this code is causing any of the issues..

FuelEdit.m

#import "FuelEdit.h"
#import "DBManager.h"

@interface FuelEdit ()

@property (nonatomic, strong) DBManager *dbManager;

@end

@implementation FuelEdit

@synthesize regoPicker;
@synthesize locationPicker;
@synthesize datePicker;

- (void)viewDidLoad
{

    self.dbManager = [[DBManager alloc] initWithDatabaseFilename:@"fuel.sql"];

    regoPicker.delegate = self;
    regoPicker.dataSource = self;
    locationPicker.delegate = self;
    locationPicker.dataSource = self;

    [regoPicker setShowsSelectionIndicator:YES];

}

- (void)viewWillAppear{

}

- (NSInteger)numberOfComponentsInPickerView: (UIPickerView *)pickerView
{
    if (pickerView == regoPicker) {

        return 1;

    } else if (pickerView == locationPicker) {

        return 1;

    } else {

        return 1;

    }
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {

    if (pickerView == regoPicker) {

    NSString *query = [NSString stringWithFormat:@"SELECT * FROM aircraft"];

    // Load the relevant data.
    NSArray *results = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];

    return results.count;

    } else if (pickerView == locationPicker) {

        NSString *query = [NSString stringWithFormat:@"SELECT * FROM location"];

        // Load the relevant data.
        NSArray *results = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];

        return results.count;

    } else {

        return 1;

    }
}

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {

    if (pickerView == regoPicker) {

        NSString *query = [NSString stringWithFormat:@"SELECT acrego FROM aircraft"];

        // Load the relevant data.
        NSArray *results = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];

        if (results !=nil) {

            return [results objectAtIndex:row];

        } else {

            return nil;

        }

    } else if (pickerView == locationPicker) {

        NSString *query = [NSString stringWithFormat:@"SELECT locname FROM location"];

        // Load the relevant data.
        NSArray *results = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];

        if (results != nil) {

            return [results objectAtIndex:row];

        } else {

            return nil;

        }

    } else {

        return 0;

    }
}

FuelEdit.h

#import <UIKit/UIKit.h>

@protocol FuelEditDelegate

@end

@interface FuelEdit : UITableViewController <UITableViewDelegate, UITableViewDataSource, UIPickerViewDataSource, UIPickerViewDelegate>

@property (nonatomic, strong) id<FuelEditDelegate> delegate;

@property (weak, nonatomic) IBOutlet UIPickerView *regoPicker;

@property (weak, nonatomic) IBOutlet UIDatePicker *datePicker;

@property (weak, nonatomic) IBOutlet UIPickerView *locationPicker;

@end

DBManager.m:

#import <Foundation/Foundation.h>
#import "DBManager.h"
#import <sqlite3.h>

@interface DBManager()

@property (nonatomic, strong) NSString *documentsDirectory;

@property (nonatomic, strong) NSString *databaseFilename;

@property (nonatomic, strong) NSMutableArray *arrResults;

-(void)copyDatabaseIntoDocumentsDirectory;

-(void)runQuery:(const char *)query isQueryExecutable:(BOOL)queryExecutable;

@end

@implementation DBManager

-(instancetype)initWithDatabaseFilename:(NSString *)system{
    self = [super init];
    if (self) {
        // Set the documents directory path to the documentsDirectory property.
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        self.documentsDirectory = [paths objectAtIndex:0];

        // Keep the database filename.
        self.databaseFilename = system;

        // Copy the database file into the documents directory if necessary.
        [self copyDatabaseIntoDocumentsDirectory];
    }
    return self;
}

-(void)copyDatabaseIntoDocumentsDirectory{
    // Check if the database file exists in the documents directory.
    NSString *destinationPath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];
    if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) {
        // The database file does not exist in the documents directory, so copy it from the main bundle now.
        NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:self.databaseFilename];
        NSError *error;
        [[NSFileManager defaultManager] copyItemAtPath:sourcePath toPath:destinationPath error:&error];

        // Check if any error occurred during copying and display it.
        if (error != nil) {
            NSLog(@"Copy Database Error: %@", [error localizedDescription]);
        }
    }
}

-(void)runQuery:(const char *)query isQueryExecutable:(BOOL)queryExecutable{
    // Create a sqlite object.
    sqlite3 *sqlite3Database;

    // Set the database file path.
    NSString *databasePath = [self.documentsDirectory stringByAppendingPathComponent:self.databaseFilename];

    // Initialize the results array.
    if (self.arrResults != nil) {
        [self.arrResults removeAllObjects];
        self.arrResults = nil;
    }
    self.arrResults = [[NSMutableArray alloc] init];

    // Initialize the column names array.
    if (self.arrColumnNames != nil) {
        [self.arrColumnNames removeAllObjects];
        self.arrColumnNames = nil;
    }
    self.arrColumnNames = [[NSMutableArray alloc] init];


    // Open the database.
    BOOL openDatabaseResult = sqlite3_open([databasePath UTF8String], &sqlite3Database);
    if(openDatabaseResult == SQLITE_OK) {
        // Declare a sqlite3_stmt object in which will be stored the query after having been compiled into a SQLite statement.
        sqlite3_stmt *compiledStatement;

        // Load all data from database to memory.
        BOOL prepareStatementResult = sqlite3_prepare_v2(sqlite3Database, query, -1, &compiledStatement, NULL);
        if(prepareStatementResult == SQLITE_OK) {
            // Check if the query is non-executable.
            if (!queryExecutable){
                // In this case data must be loaded from the database.

                // Declare an array to keep the data for each fetched row.
                NSMutableArray *arrDataRow;

                // Loop through the results and add them to the results array row by row.
                while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
                    // Initialize the mutable array that will contain the data of a fetched row.
                    arrDataRow = [[NSMutableArray alloc] init];

                    // Get the total number of columns.
                    int totalColumns = sqlite3_column_count(compiledStatement);

                    // Go through all columns and fetch each column data.
                    for (int i=0; i<totalColumns; i++){
                        // Convert the column data to text (characters).
                        char *dbDataAsChars = (char *)sqlite3_column_text(compiledStatement, i);

                        // If there are contents in the currenct column (field) then add them to the current row array.
                        if (dbDataAsChars != NULL) {
                            // Convert the characters to string.
                            [arrDataRow addObject:[NSString  stringWithUTF8String:dbDataAsChars]];
                        }

                        // Keep the current column name.
                        if (self.arrColumnNames.count != totalColumns) {
                            dbDataAsChars = (char *)sqlite3_column_name(compiledStatement, i);
                            [self.arrColumnNames addObject:[NSString stringWithUTF8String:dbDataAsChars]];
                        }
                    }

                    // Store each fetched data row in the results array, but first check if there is actually data.
                    if (arrDataRow.count > 0) {
                        [self.arrResults addObject:arrDataRow];
                    }
                }
            }
            else {
                // This is the case of an executable query (insert, update, ...).

                // Execute the query.
                //BOOL executeQueryResults = sqlite3_step(compiledStatement);
                //if (executeQueryResults == SQLITE_DONE) {
                if (sqlite3_step(compiledStatement)) {
                // Keep the affected rows.
                    self.affectedRows = sqlite3_changes(sqlite3Database);

                    // Keep the last inserted row ID.
                    self.lastInsertedRowID = sqlite3_last_insert_rowid(sqlite3Database);
                }
                else {
                    // If could not execute the query show the error message on the debugger.
                    NSLog(@"DB Error: %s", sqlite3_errmsg(sqlite3Database));
                }
            }
        }
        else {
            // In the database cannot be opened then show the error message on the debugger.
            NSLog(@"%s", sqlite3_errmsg(sqlite3Database));
        }

        // Release the compiled statement from memory.
        sqlite3_finalize(compiledStatement);

    }

    // Close the database.
    sqlite3_close(sqlite3Database);
}

-(NSArray *)loadDataFromDB:(NSString *)query{
    // Run the query and indicate that is not executable.
    // The query string is converted to a char* object.
    [self runQuery:[query UTF8String] isQueryExecutable:NO];

    // Returned the loaded results.
    return (NSArray *)self.arrResults;
}

-(void)executeQuery:(NSString *)query{
    // Run the query and indicate that is executable.
    [self runQuery:[query UTF8String] isQueryExecutable:YES];
}


@end

2 Answers2

1

Make sure your results contain only one array it must not be list of array. I am feeling like when you are doing this

return [results objectAtIndex:row];

it is returning a array instead of returning a string. May be your results contain list of arrays. Please verify this thing.

Shehzad Ali
  • 1,846
  • 10
  • 16
  • lol I am wondering i answered first and saying the same thing which was answered later and accepted. Don't know why people downvote the correct things without giving you valid reason. – Shehzad Ali Feb 10 '16 at 08:08
  • I know you can't cast vote :) But its wrong thing I answered first and I answered the right thing but someone casted a downvote against it. – Shehzad Ali Feb 10 '16 at 09:02
-1

Error is for the unrecognized selector. It means you are calling length function for an array or returning Array instead of string in any of the method.

I have checked your code.Please check in following section:

    NSArray *results = [[NSArray alloc] initWithArray:[self.dbManager loadDataFromDB:query]];

    if (results !=nil) {

        return [results objectAtIndex:row];

    }

I think [results objectAtIndex:row] is returning a array instead of the string.

Please let me know the results...

Nirav Gadhiya
  • 6,342
  • 2
  • 37
  • 76