Utiliser SQLite

Pour ce 14ème tutoriel, nous allons voir comment lire et écrire dans une base de données grâce à sqlite.
Afin d’essayer de rendre ce tutoriel utile au plus grand nombre, j’ai décidé de montrer comment enregistrer le score d’un joueur puis afficher les trois meilleurs dans des labels.

Nous obtiendrons ceci :

Pour expliquer rapidement, on peut entrer le nom du joueur dans un textfield, générer un score avec un bouton puis enregistrer et actualiser les meilleurs scores.

1) Créer le fichier sqlDatabase.sql

C’est ce fichier qui va contenir physiquement la base de données. On pourra ensuite le lire, le trier et l’éditer.
Pour le créer, il faut utiliser le Terminal (spotlight-> »terminal »). Placez-vous dans le dossier désiré et pour créer le fichier voici les commandes nécessaires :

//création du fichier
sqlite3 sqlDatabase.sql
 
//création de la Base de Données (BDD)
CREATE TABLE score ( id INTEGER PRIMARY KEY, nom VARCHAR(50), scoreValue INT);
INSERT INTO score (nom, scoreValue) VALUES ('iPodishima', '5');
INSERT INTO score (nom, scoreValue) VALUES ('Heyfeel', '4');
INSERT INTO score (nom, scoreValue) VALUES ('Aurel', '3');
INSERT INTO score (nom, scoreValue) VALUES ('Bidou', '2');

Il faut maintenant mettre ce fichier dans les ressources d’un nouveau projet Xcode View-Based application nommé Score. Faire « copy items into destination group’s folder ».

2) Édition du projet et interface builder

Tout d’abord il faut importer le framework sqlite qui n’est pas vraiment aussi simple que les autres à ajouter : Faire « add->existing frameworks » ensuite avec le Finder cherchez un fichier qui se nomme “libsqlite3.0.dylib”. Si vous en trouvez plusieurs, le bon est celui qui se trouve ici :
“/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk/usr/lib/”
Il suffit ensuite de l’ajouter.

http://www.ipup.fr/forum/userimages/tuto14IB.jpg

Les déclarations de l’interface Builder dans ScoreViewController.h

@interface ScoreViewController : UIViewController <UITextFieldDelegate> {
IBOutlet UILabel *lblScore;
IBOutlet UILabel *mScoreUn;
IBOutlet UILabel *mScoreDeux;
IBOutlet UILabel *mScoreTrois;
IBOutlet UITextField *textField;
}

ScoreViewController.m

int scoreI=0;
 
-(BOOL)textFieldShouldReturn:(UITextField *)leTextField {
if (leTextField == textField )
{
[textField resignFirstResponder];
}
return YES;
}
 
-(IBAction)incrementScore:(id)sender{
scoreI++;
//Affichage du score
lblScore.text = [[NSString alloc] initWithFormat:@"Score = %d",scoreI] ;
}
 
-(IBAction)incrementScore:(id)sender{
scoreI++;
//Affichage du score
lblScore.text = [[NSString alloc] initWithFormat:@"Score = %d",scoreI] ;
}

3) Lecture dans dans base

Il faut commencer par créer une nouvelle classe « NSObject subclass » nommée Score.
Cette classe va nous permettre de créer un objet Score qui contiendra les valeurs de la BDD.

Score.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>
 
@interface Score : NSObject {
NSInteger primaryKey;
NSString *nom;
NSInteger scoreValue;
}
 
@property (assign, nonatomic, readonly) NSInteger primaryKey;
@property (nonatomic, retain) NSString *nom;
@property (assign, nonatomic, readonly) NSInteger scoreValue;
 
-(id)initWithName:(NSString *)n scoreValue:(NSInteger )s;
 
@end

Score.m

#import "Score.h"
@implementation Score
@synthesize nom,
scoreValue,
primaryKey;
 
-(id)initWithName:(NSString *)n scoreValue:(int )s {
self.nom = n;
scoreValue = s;
return self;
}
 
@end

Il faut maintenant créer une nouvelle classe NSObject subclass qui va permettre d’administrer la BDD. Vous pouvez la nommer SQLmanager :  je me suis servi du tutoriel disponible ici auquel j’ai ajouté l’écriture comme on le verra par la suite.

SQLmanager.h

#import <Foundation/Foundation.h>
#import <sqlite3.h>    // Import du framework sqlite
#import "Score.h"    // Import de l'objet Score
 
@interface SQLManager : NSObject {
// Variables de la Base de Données
NSString *databaseName;
NSString *databasePath;
 
// Tableau de Scores
NSMutableArray *scores;   
}
 
@property (nonatomic, retain) NSMutableArray *scores;
 
-(id) initDatabase;
-(void) checkAndCreateDatabase;
-(void) readScoresFromDatabase;
 
@end

SQLmanager.m

#import "SQLManager.h"
 
 
@implementation SQLManager
@synthesize scores;
 
-(id) initDatabase{
//On définit le nom de la base de données
databaseName = @"sqlDatabase.sql";
 
// On récupère le chemin
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [documentPaths objectAtIndex:0];
databasePath = [documentsDir stringByAppendingPathComponent:databaseName];
 
return self;
}
 
-(void) checkAndCreateDatabase{
// On vérifie si la BDD a déjà été sauvegardée dans l'iPhone de l'utilisateur
BOOL success;
 
// Crée un objet FileManagerCreate qui va servir à vérifer le status
// de la base de données et de la copier si nécessaire
NSFileManager *fileManager = [NSFileManager defaultManager];
 
// Vérifie si la BDD a déjà été créée  dans les fichiers system de l'utilisateur
success = [fileManager fileExistsAtPath:databasePath];
 
// Si la BDD existe déjà "return" sans faire la suite
if(success) return;
 
// Si ce n'est pas le cas alors on copie la BDD de l'application vers les fichiers système de l'utilisateur
 
// On récupère le chemin vers la BDD dans l'application
NSString *databasePathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:databaseName];
 
// On copie la BDD de l'application vers le fichier systeme de l'application
[fileManager copyItemAtPath:databasePathFromApp toPath:databasePath error:nil];
 
[fileManager release];
}
 
-(void) readScoresFromDatabase {
// Déclaration de l'objet database
sqlite3 *database;
 
// Initialisation du tableau de score
scores = [[NSMutableArray alloc] init];
 
// On ouvre la BDD à partir des fichiers système
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Préparation de la requête SQL qui va permettre de récupérer les objets score de la BDD
//en triant les scores dans l'ordre décroissant
const char *sqlStatement = "select * from score ORDER BY scoreValue DESC";
 
//création d'un objet permettant de connaître le status de l'exécution de la requête
sqlite3_stmt *compiledStatement;
 
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// On boucle tant que l'on trouve des objets dans la BDD
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// On lit les données stockées dans le fichier sql
// Dans la première colonne on trouve du texte que l'on place dans un NSString
NSString *aName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, 1)];
// Dans la deuxième colonne on récupère le score dans un NSInteger
NSInteger aScore = sqlite3_column_int(compiledStatement, 2);
 
// On crée un objet Score avec les pramètres récupérés dans la BDD
Score *score = [[Score alloc] initWithName:aName scoreValue:aScore];
 
// On ajoute le score au tableau
[scores addObject:score];
[score release];
}
}
// On libère le compiledStamenent de la mémoire
sqlite3_finalize(compiledStatement);
 
}
//On ferme la BDD
sqlite3_close(database);
 
}
 
 
@end

Nous allons maintenant voir comment utiliser ce code en réalisant l’affichage des trois meilleurs scores.

Dans le ScoreViewController.m

#import "ScoreViewController.h"
#import "ScoreAppDelegate.h"
#import "Score.h"
#import "SQLManager.h"
 
@implementation ScoreViewController
 
- (void)viewDidLoad {
[super viewDidLoad];
// initialisation de la BDD
sqlManager = [[SQLManager alloc] initDatabase];
//Vérification et création de la BDD
[sqlManager checkAndCreateDatabase];
//Lecture des scores et création du tableau
[sqlManager readScoresFromDatabase];
//On appelle l'affichage dans les labels
[self afficherScore];
 
}
 
-(void)afficherScore{
//On affiche les trois premiers scores du tableau
//création d'un objet score qui est égal au premier du tableau
Score *score = (Score *)[sqlManager.scores objectAtIndex:0];
//on
écrit dans le label le nom et le scoreValue du premier Score du
tableau, c'est le meilleur puisqu'on a trié dans l'ordre décroisant
mScoreUn.text = [score.nom  stringByAppendingString: [[NSString alloc] initWithFormat:@"\t \t %d points", score.scoreValue]];
score = (Score *)[sqlManager.scores objectAtIndex:1];
//même chose pour le deuxième et le troisième Score
mScoreDeux.text = [score.nom  stringByAppendingString: [[NSString alloc] initWithFormat:@"\t \t %d points", score.scoreValue]];
score = (Score *)[sqlManager.scores objectAtIndex:2];
mScoreTrois.text = [score.nom  stringByAppendingString: [[NSString alloc] initWithFormat:@"\t \t %d points", score.scoreValue]];
}

Dans ScoreViewController.h il faut déclarer un SQLManager :

SQLManager *sqlManager;

4) Ecriture dans dans base

Il faut ajouter une méthode au SQLManager

- (void)insertIntoDatabase:(Score*)newScore {
 
//initialisiation de la BDD
[self initDatabase];
 
//Vérification de la BDD
[self checkAndCreateDatabase];
 
// Déclaration de l'objet database
sqlite3 *database;
// On ouvre la BDD à partir des fichiers système
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Préparation de la requête SQL qui va permettre d'ajouter un score à la BDD       
NSString *sqlStat = [[[NSString alloc] initWithFormat:@"INSERT INTO score (nom, scoreValue) VALUES ('%@', '%d');",newScore.nom, newScore.scoreValue] autorelease];
//Ici c'est pas génial comme méthode donc si vous trouvez mieux je suis prenneur
const char *sqlStatement = [sqlStat cStringUsingEncoding : [NSString defaultCStringEncoding]];
//On utilise sqlite3_exec qui permet très simplement d'exécuter une requète sur la BDD
sqlite3_exec(database, sqlStatement,NULL,NULL,NULL);
}
sqlite3_close(database);
}

Et on ajoute l’appel dans le ScoreViewController

-(IBAction)saveScore:(id)sender{
Score *score = [[Score alloc] initWithName:textField.text scoreValue:scoreI];
[sqlManager insertIntoDatabase:score];
[sqlManager readScoresFromDatabase];
[self afficherScore];
}

conclusion

Si ça fonctionne « Well Done » sinon vous pouvez vous aider des sources et venir poser vos questions sur le forum.