티스토리 뷰

데이터베이스 만들기


시작하기위해, FireFox와 SQLite Database Manager 플러그인이 필요합니다. 만약 없다면, FireFox 웹사이트에서 다운 및 설치 가능합니다. FireFox가 설치되면 애드온 관리자에서 SQLite Manager를 설치합시다. 

SQLite Manager는 사용중인 버젼에 따라 Firefox 메뉴나 도구메뉴로부터 실행가능합니다  (그림 1).

Figure 1: SQLite Manager in Firefox
그림 1: Firefox 도구 SQLite Manager 모습 

새 데이터베이스를 만들기위해 new Database 버튼을 클릭합시다 (그림 2). 원하는 데이터베이스 이름 입력합시다. SQLite 확장자는 자동적으로 추가됩니다. 파일 저장을 확인받습니다 (당연히). 나중에 파일을 복사해야 하므로 어디에 저장하는지 기억해둡시다.

다음으로, new table 버튼을 클릭하여 (그림 3) 새 테이블을 만드는데, 역시 이름을 정해줍시다. 이 강좌에선, wineTbl 테이블과 다음의 4개의 컬럼을 쓰도록 합니다: id [primary, autoinc, integer], winename [varchar], winerating [varchar] 그리고 wineimage [blob].

Figure 2: Create a table
그림 2: 테이블 만들기
Figure 3: Create the necessary columns
그림 3: 필요한 컬럼 만들기

이 강좌의 sake에 대해, 와인 데이터베이스와 웹으로부터 이미지들을 준비해놓을 것입니다. 테이블을 선택하고 browse & data 탭을 선택하여 데이터를 추가할 수 있습니다. 이미지 업로드 하려면 blob 필드 다음의 문서클립 아이콘을 클릭합니다. (그림 4와 그림 5).


이제 FireFox 메뉴에서 데이터베이스를 닫을 수 있으며 이 강좌에선 이후로 FireFox를 사용할 일은 없습니다.

Figure 4: Adding a new record in the database
그림 4: 데이터베이스에 새 레코드 추가
Figure 5: Record listing in the database
그림 5: 데이터베이스 레코드 목록보기

IOS 5 프로젝트 만들기


XCode 4.2 실행하고 Single-View IOS 5 앱을 만듭시다. 이름을 부여하고 Storyboard와 ARC를 선택합시다. Git 설정하여 source control 사용여부는 알아서하고 프로젝트 만들기를 끝냅시다. (그림 6).

Figure 6: The Wine List App
그림 6: Wine List 앱

SQLite 설정


Frameworks 폴더를 확장하고, 프레임웍중 하나를 오른쪽 클릭하고 Show in Finder를 선택하여 프레임웍 위치를 파인더로 엽니다. libsqlite_3.0.dylib 파일을 프로젝트에 추가해야합니다 (그림 6), 사용자 폴더가 나올때까지 2또는 3단계 위로 이동합니다 (파인더 메뉴의 둘러싸고 있는 폴더로 가봅시다). 그것을 열고 lib 폴더를 엽니다. sqlite_3.0.lib를 찾을때까지 아래로 스크롤. 프레임웍 폴더로 파일이 복사되지 않게 주의하며 Frameworks에 파일을 드래그하고 참조(reference)만 만듭니다 (그림 7).


프로젝트 루트를 선택하고, 오른쪽 클릭 후 Show in Finder 선택합니다. 이 강좌의 처음에 만든 sql 데이터베이스를 위치시키고 프로젝트 헤더와 구현 파일들이 있는 프로젝트 그룹으로 복사하여 넣습니다 (그림 8).

Figure 7: Copy Reference of sqlite3.0.dylib to the Framework folder
그림 7: sqlite3.0.dylib의 참조를 Framework 폴더에 복사
Figure 8: Copy database file to the project folder
그림 8: 프로젝트 폴더에 데이터베이스 파일 복사

DAO(데이터베이스 접속) 기능 설정


(File | New Group) 또는 (Context Menu | New Group)을 통해 “Model”이란 이름으로 새 그룹을 만듭니다. 두개의 Objective-C 구현 파일과 대응하는 헤더 파일들을 만듭니다. File 메뉴 또는 Context 메뉴 | New File 선택으로 Model 그룹을 선택합니다. Objective-C 노드 선택하면 Objective-C 클래스 템플릿입니다.


WineList (이 강좌를 따라하고 있다면)라는 이름, NSObject의 Subclass 선택하고 파일을 만듭니다. 같은 방법으로 다음의 파일셋을 만듭니다: MyWineList, (WinesDAO 처럼 만들지는 선택할 수 있습니다). 다시한번 NSObject의 Subclass 선택하고 파일을 만듭니다 (그림 9~10).

WineList 클래스는 WineList.h (header) 파일에 4개의 속성을 지니며, wineTbl에 있는 컬럼들은 다음과 같습니다 (그림 11):

  • wineId
  • wine
  • rating
  • photo


게터 세터 설정을 위해 WineList.m (구현) 파일을 엽니다. WineList는 4개의 @synthesize 구문을 타이핑해 넣읍시다.

  • @synthesize wineId;
  • @synthesize wine;
  • @synthesize rating;
  • @synthesize photo;

Figure 9: Create the WineList class
그림 9: WineList 클래스 만들기
Figure 10: Create the WineLists class
그림 10: WineLists 클래스 만들기
Figure 11: The WineList header
그림 11: WineList header

CRUD(참조/검색/갱신) 기능 만들기


CRUD는 확장의 조각입니다. 이 강좌에선 R(읽기) 작동만 구현합니다. 그럴려면 앱은 CRUD(읽기) 작동을 위한 DAO 클래스를 필요로 하게 됩니다, 만약 이미 DAO 클래시를 포함시키지 않았다면, MyWineLists 또는 무엇이든간에 선언과 구현이 작동하도록 새 Objective-C 클래스를 만듭니다. MyWineLists 헤더파일에 대해서는, sqlite3 객체와 NSMutableArray 메쏘드가 선언되었습니다 (그림 12):

  • db
  • getMyWines


이 객체들 구현을 위해, MyWineLists.m 파일을 엽니다. 이 파일에서는, the gut if the operations will take place.

시작을 위해 NSMutableArray 메쏘드 getMyWines 와 배열 포인터 변수 만들기:

  • wineArray


다음은 아래의 NSFileManager 객체, NSString 객체, Bool 객체를 선언:

  • fileMgr
  • dbPath
  • success



NSMutableArray *wineArray = [[NSMutableArray alloc] init];
@try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:@"IOSDB.sqlite"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
...

dbPath는 파일명과 fileMgr에 SQLite 데이터베이스의 경로를 포함하여 넘겨줄 것입니다. 만약 파일이 위치되면, success 는 true 값을 가집니다. 다음은 파일이 위치되었는지와 오류를 기록하지 않는지 테스트 해봅니다. 연계 동작은 데이터베이스를 열어보려 할것입니다, Select 표현식과 sql3_stmt 설정 전에 sqlite3_open:

  • sql
  • sqlStatement



if(!success)
{
NSLog(@"Cannot locate database file '%@'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(@"An error has occured.");
}
const char *sql = "SELECT id, Wine, Rating, Photo FROM WineTbl";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(@"Problem with prepare statement");
}
...

데이터베이스가 성공적으로 열리면, sqlite3_prepare는 sqlStatement를 실행하려 할것이다. 만약 표현식이 결과셋이 반환되는 결과가 성공적으로 실행되면 NSMutableArray 필드들에 결과 값들이 설정되는 while 루프가 실행됩니다. 

...
while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
WineList *MyWine = [[WineList alloc]init];
MyWine.wineId = sqlite3_column_int(sqlStatement, 0);
MyWine.wine = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
MyWine.rating = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
const char *raw = sqlite3_column_blob(sqlStatement, 3);
int rawLen = sqlite3_column_bytes(sqlStatement, 3);
NSData *data = [NSData dataWithBytes:raw length:rawLen];
MyWine.photo = [[UIImage alloc] initWithData:data];
[wineArray addObject:MyWine];
}
}
@catch (NSException *exception) {
NSLog(@"An exception occured: %@", [exception reason]);
}
@finally {
return wineArray;


...

이것은 cRud 동작들을 잘 관리합니다. 다음단계는 IBActions 와 IBOutlets 연결들을 만들어서 UI 설정을 연관시킬것입니다. (그림 12, 13).

Figure 12: The implementation of WineLists
그림 12: MyWineLists 헤더
Figure 13: The CRUD operations
그림 13: MyWineLists 구현파일 안 CRUD 동작구현

UI 기능 만들기


시작은 파일을 열고 자리잡기. 단독 공백 장면이 보일겁니다 (View Controller). 이 부분에서는, 4개의 라벨(UILabel)이 필요합니다: Wine Name(와인 이름)과 데이터베이스 값, NSMutableArray에 저장 될 Wine Rating(와인 평가)와 상응하는 데이터베이스 값. 이미지들에 대해서는, 장면에 UIImageView 를 드래그. 사용자 인터페이스 마지막 단계로, UIToolbar를 장면 아래에 위치시키고 포함된 버튼 이름을 Next Bottle로 변경합시다 (그림 14).

Figure 14: Connecting the dots
Figure 14: 점 연결
Figure 15: The project structure
그림 15: 프로젝트 구조

앱 종결을 위해, ViewController 헤더와 구현 파일에 몇가지 코드 추가가 필요합니다. IBAction과 IBOutlet 설정을 위해, Xcode 툴바 오른쪽 위에 있는 얼굴 아이콘 Assistant Editor를 클릭하여 storyboard 옆에 헤더 파일을 엽시다 (단축키 Command+Alt+Enter) (그림 14). 처음 label을 선택하고 헤더파의 중괄호 끝과 @end directive사이에 연결선을 드래그합시다 (Ctrl+왼쪽 마우스 버튼). 팝업이뜨면, IBOutlet 선택하고 이름은 winename로 지정합시다. 평가 정보를 포함할 두번째 label도 IBOutlet 으로 하고 이름은 winerating으로 합시다. 이미지도 같은작업 반복하여 IBOutlet  만들고 이름은 wineViewer로 합시다. 마지막으로 툴바 버튼을 드래그하여 연결하는데 IBAction으로 만들고 메쏘드 이름은 GetWineListing으로 합시다. 또한 NSMutableArray 객체도 추가합시다:

  • wines


점은 연결이 되었음을 가리키는 의미로 채워져 있어야 합니다.

구현 파일을 열고 게터 세터 설정:


@synthesize wineViewer;
@synthesize winename;
@synthesize winerating;
@synthesize wines;


앱 초기화 종료시 호출되는 viewDidLoad 에서는 배열에서 초기 데이터 고정을 위해 포인터를 추가하면 앱이 어떤 정보와 index 0에 있는 이미지를 표시할 것이다.



- (void)viewDidLoad
{
MyWineLists * mywines =[[MyWineLists alloc] init];
self.wines = [mywines getMyWines];
[self.wineViewer setImage:((WineList *) [self.wines objectAtIndex:0]).photo];
[self.winename setText:((WineList *) [self.wines objectAtIndex:0]).wine];

[self.winerating setText:((WineList *) [self.wines objectAtIndex:0]).rating];

[super viewDidLoad];
}
...

viewDidUnload 에서는 속성들에 nil 값을 줘서 메모리에서 해제합니다


- (void)viewDidUnload
{
[self setWineViewer:nil];
[self setWinename:nil];
[self setWinerating:nil];
[super viewDidUnload];
}
...

마지막으로 GetWineListing 메쏘드를 사용자가 버튼 클릭시, index가 증가값을 얻고 선택된 index 번호에서 데이터를 검색하도록 구현합니다.


- (IBAction)GetWineListing:(id)sender {
static NSInteger currentIndex = 0;
if (++currentIndex == [self.wines count]) {
currentIndex=0;

}else{
WineList *aWine = (WineList *) [self.wines objectAtIndex: currentIndex];
[self.winename setText:aWine.wine];
[self.winerating setText:aWine.rating];
[self.wineViewer setImage:aWine.photo];
}
}

앱 테스트


좋습니다 이제 끝입니다. 앱 실행을 위해 실행 버튼을 클릭합시다. 앱 설치가 완료된 후 화면에 데이터와 이미지가 있어야 합니다. 다음 목록을 보기 위해 Next Bottle을 클릭해보세요.

Figure 15: The running app
그림 15: 실행중인 앱

소스코드


아래는 만들었던 다양한 파일들의 소스입니다.

WineList.m

//
//  WineList.m
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
 
#import "WineList.h"
 
@implementation WineList
@synthesize wineId;
@synthesize wine;
@synthesize rating;
@synthesize photo;
 
//With ARC, if you selected id, you don't need to dealloc
 
@end

MyWineLists

//
//  MyWineLists.h
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
 
#import <Foundation/Foundation.h>
#import <sqlite3.h>
 
@interface MyWineLists : NSObject{
    sqlite3 *db;
}
 
- (NSMutableArray *) getMyWines;
 
@end

WineList.h

//
//  WineList.h
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
 
#import <Foundation/Foundation.h>
 
@interface WineList : NSObject{
    NSInteger wineId;
    NSString *wine;
    NSString *rating;
    UIImage *photo;
}
 
@property (nonatomic,retain)NSString *wine;
@property (nonatomic, assign) NSInteger wineId;
@property (nonatomic, retain)NSString *rating;
@property (nonatomic, retain) UIImage *photo;
 
 
@end

MyWineLists.m

//
//  MyWineLists.m
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
 
#import "MyWineLists.h"
#import "WineList.h"
 
@implementation MyWineLists
- (NSMutableArray *) getMyWines{
    NSMutableArray *wineArray = [[NSMutableArray alloc] init];
    @try {
        NSFileManager *fileMgr = [NSFileManager defaultManager];
        NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:@"IOSDB.sqlite"];
        BOOL success = [fileMgr fileExistsAtPath:dbPath];
        if(!success)
        {
            NSLog(@"Cannot locate database file '%@'.", dbPath);
        }
        if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
           {
               NSLog(@"An error has occured.");
           }
        const char *sql = "SELECT id, Wine, Rating, Photo FROM  WineTbl";
        sqlite3_stmt *sqlStatement;
        if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
           {
               NSLog(@"Problem with prepare statement");
           }
         
        //
        while (sqlite3_step(sqlStatement)==SQLITE_ROW) {
            WineList *MyWine = [[WineList alloc]init];
            MyWine.wineId = sqlite3_column_int(sqlStatement, 0);
            MyWine.wine = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement,1)];
            MyWine.rating = [NSString stringWithUTF8String:(char *) sqlite3_column_text(sqlStatement, 2)];
            const char *raw = sqlite3_column_blob(sqlStatement, 3);
            int rawLen = sqlite3_column_bytes(sqlStatement, 3);
            NSData *data = [NSData dataWithBytes:raw length:rawLen];
            MyWine.photo = [[UIImage alloc] initWithData:data];
            [wineArray addObject:MyWine];
        }
    }
    @catch (NSException *exception) {
        NSLog(@"An exception occured: %@", [exception reason]);
    }
    @finally {
        return wineArray;
    }
     
     
}
 
 
@end

kcbViewController

//
//  kcbViewController.h
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
 
#import <UIKit/UIKit.h>
 
@interface kcbViewController : UIViewController{
    NSMutableArray *wines;
 
     
}
 
@property(nonatomic,retain) NSMutableArray *wines;
@property (weak, nonatomic) IBOutlet UIImageView *wineViewer;
@property (weak, nonatomic) IBOutlet UILabel *winename;
@property (weak, nonatomic) IBOutlet UILabel *winerating;
- (IBAction)GetWineListing:(id)sender;
 
 
@end

kcbViewController.m

//
//  kcbViewController.m
//  MyWineList
//
//  Created by Kevin Languedoc on 11/25/11.
//  Copyright (c) 2011 kCodebook. All rights reserved.
//
#import "kcbViewController.h"
#import "WineList.h"
#import "MyWineLists.h"
 
@implementation kcbViewController
@synthesize wineViewer;
@synthesize winename;
@synthesize winerating;
@synthesize wines;
 
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}
 
#pragma mark - View lifecycle
 
- (void)viewDidLoad
{
    MyWineLists * mywines =[[MyWineLists alloc] init];
    self.wines = [mywines getMyWines];
    [self.wineViewer setImage:((WineList *) [self.wines objectAtIndex:0]).photo];
    [self.winename setText:((WineList *) [self.wines objectAtIndex:0]).wine];
     
    [self.winerating setText:((WineList *) [self.wines objectAtIndex:0]).rating];
      
    [super viewDidLoad];
}
 
- (void)viewDidUnload
{
    [self setWineViewer:nil];
    [self setWinename:nil];
    [self setWinerating:nil];
    [super viewDidUnload];
}
 
- (IBAction)GetWineListing:(id)sender {
    static NSInteger currentIndex = 0;
    if (++currentIndex == [self.wines count]) {
        currentIndex=0;
}else{
        WineList *aWine = (WineList *) [self.wines objectAtIndex: currentIndex];
        [self.winename setText:aWine.wine];
        [self.winerating setText:aWine.rating];
        [self.wineViewer setImage:aWine.photo];
    }
}
 
- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}
 
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}
 
- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}
 
- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}
 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
 
 
 
 
@end

댓글