วันศุกร์ที่ 26 พฤศจิกายน พ.ศ. 2553

CoreData Multiple Entity Relationship

สำหรับ Entry นี้จะเป็นเรื่องของการสร้าง Entity มากกว่า 1 ตัวนะครับ
ใน Entry นี้จะไม่ทำเรื่องของ UI นะครับ
จะแสดงแค่วิธีการ เอาไปประยุกต์เองนะครับ

เริ่มจากการเพิ่ม Entity ใน File CoreDataFromTemplate.xcdatamodel
(File .xcdatamodel ที่เราออกแบบตัวเก็ยข้อมูล ดูได้ใน Entry เก่าครับ)
ผมจะเพิ่ม Entity เข้าไป 2 ตัวนะครับ เป็น A กับ B
และทั้ง A และ B จะมี Property name นะครับ



เราจะทำ Relation ระหว่าง A และ B นะครับ
รูปแบบก็เป็น A has_many B ละกันครับ

เริ่มจากเพิ่ม Relationship ใน A และ B ครับ
เลือก Entity A แล้วไปที่ส่วนของ Property กดที่เครื่องหมาย + เลือก Add Relationship
ผมตั้งชื่อว่า bList นะครับ Destination เลือก Entity B เลือกช่อง To-Many Relationship




หลังจากนั้นเลือก Entity B ครับ
เพิ่ม Relationship ใน Property ครับ
ผมตั้งชื่อว่า a นะครับ Destination เลือก Entity A และ Inverse เลือก bList ครับ



Save ครับ

สร้าง File ใหม่ครับ เลือก Add -> New File… เลือก Managed Object Class



ที่หน้าของ Managed Object Class Generation เลือก Entity A และ B กด Finish



จะได้ File เพิ่มมา 4 File 



เริ่มจากเข้าไปที่ MainViewController.h ทำการ Import File ที่เพิ่มขึ้นมา

#import "A.h"
#import "B.h"



ใน MainViewController.m ที่ Method viewDidLoad
ทำการสร้าง A โดยให้ชื่อ A1

A *a = (A *)[NSEntityDescription insertNewObjectForEntityForName:@"A" inManagedObjectContext:managedObjectContext];
a.name = @"A1";
NSError *error;
if (![managedObjectContext save:&error]) {
}



สร้าง Entity B โดยใส่ Entity B เข้าไปใน A ที่สร้างขึ้น
จะมี Method addBListObject: ซึ่งเป้น Method ที่ถูก Generate สามารถดูได้ใน A.h ครับ

B *b = (B *)[NSEntityDescription insertNewObjectForEntityForName:@"B" inManagedObjectContext:managedObjectContext];
b.name = @"B1";
[a addBListObject:b]; // <<<<<
if (![managedObjectContext save:&error]) {
}
b = (B *)[NSEntityDescription insertNewObjectForEntityForName:@"B" inManagedObjectContext:managedObjectContext];
b.name = @"B2";
[a addBListObject:b]; // <<<<<
if (![managedObjectContext save:&error]) {
}



ผมสร้าง B ขึ้นมา 2 ตัวนะครับ B1 และ B2 ใส่เข้าไปใน Object A

การแสดงผล เริ่มจากการเรียกข้อมูล A ก่อน

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"A" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSMutableArray *aArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (aArray == nil) {
}
[request release];
[aArray release];



หลังจากนั้นในแต่ละ Entity A จะมี B อยู่ทำการเรียกข้อมูล B จาก A

NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"A" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSMutableArray *aArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (aArray == nil) {
}
[request release];
// ********************* Begin *************************
NSManagedObject *selectObject;
NSPredicate *predicate;
for (A *a in aArray) {
NSLog(@"*** A %@ ***\n", a.name);
selectObject = a;
predicate = [NSPredicate predicateWithFormat:@"a == %@", selectObject];
request = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription entityForName:@"B" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
[request setPredicate:predicate];
NSMutableArray *bArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (bArray == nil) {
}
for (B *b in bArray) {
NSLog(@"B %@", b.name);
}
[request release];
[bArray release];
}
// *********************** End **************************
[aArray release];



จะแสดงข้อมูล A และ B ที่อยู่ใน A ออกมาครับ
การลบนะครับ จะมี Method removeBListObject อยู่ครับ
(ผมแทรกหลัง For ของการแสดง B ครับ)

// ********************* Begin ***********************
selectObject = [aArray objectAtIndex:0];
predicate = [NSPredicate predicateWithFormat:@"a == %@", selectObject];
request = [[NSFetchRequest alloc] init];
entity = [NSEntityDescription entityForName:@"B" inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
[request setPredicate:predicate];
NSMutableArray *bArray = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (bArray == nil) {
}
[(A *)selectObject removeBListObject:[bArray objectAtIndex:0]];
[request release];
[bArray release];
// ********************** End ***********************



ถ้าใส่ Code การแสดงผลอีกที B1 จะหายไปครับ
(รูปสุดท้ายเป็น Code ทั้งหมดที่เพิ่มใน viewDidLoad โดยยังไม่มีการแสดงผลรอบ 2 นะครับ)






วันศุกร์ที่ 19 พฤศจิกายน พ.ศ. 2553

CoreData Basic CRUD

ต่อจาก Entry ที่แล้ว นะครับ

สำหรับ Entry นี้จะเป็นเรื่องของ CRUD (Create Read Update Delete) นะครับ
โดยจะต่อจาก Entry ที่แล้วนะครับ

จาก Entry ที่แล้วเราสามารถนำข้อมูลเข้า CoreData ได้แล้ว

-(IBAction) addNew{
MyEntity *myEntity = (MyEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" 
inManagedObjectContext:managedObjectContext];
myEntity.name = _name.text;
NSError *error;
if (![managedObjectContext save:&error]) {
//Handle the error.
}
}

ครั้งนี้เราจะเริ่มจากการนำข้อมูลออกมาแสดงนะครับ
โดยเริ่มผมจะแสดงข้อมูลใน UITableView นะครับ

ขั้นแรกเราก็ทำการสร้าง UITableView ขึ้นมาก่อนนะครับ
เปิด File MainViewController.h

#import <UIKit/UIKit.h>
#import "MyEntity.h"

@interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> { // <<<< Add Protocol. 
IBOutlet UITextField *_name;
NSManagedObjectContext *managedObjectContext
UITableView *_tableView; // <<<< Add This Line.
}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

-(IBAction) addNew;
-(IBAction) showData;

@end



สำหรับเรื่องของ UITableView คราวหน้าจะมาเขียนให้นะครับ
คร่าวๆ คือต้องสร้าง tableView และทำการประกาศ Protocol 2 ตัว (เป็นเรื่องของ Delegate Method ลองหาอ่านเพิ่มเติมได้ครับ) 
สร้าง tableView จาก UITableView และมี Protocol ที่เกี่ยวข้องคือ UITableViewDelegate และ UITableViewDataSource

หลังจากนั้นไปที่ MainViewController.m
ทำการสร้าง Table 

- (void)viewDidLoad {
    [super viewDidLoad];
_tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 300, 400) style:UITableViewStylePlain]; // <<<< Add This Line.
_tableView.center = self.view.center; // <<<< Add This Line.
_tableView.delegate = self; // <<<< Add This Line.
_tableView.dataSource = self; // <<<< Add This Line.
[self.view addSubview:_tableView]; // <<<< Add This Line.
}



เพิ่ม Method สำหรับการ Get ข้อมูล (ในที่นี้ให้เป็น Method ที่ใช้งานภายใน Class MainViewController)

#import "MainViewController.h"

@interface MainViewController (Private)

-(NSArray *)getData;

@end


@implementation MainViewController

@synthesize managedObjectContext; 

-(NSArray *)getData{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" 
  inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
[request release];
return [mutableFetchResults autorelease];
}



ทำการ Implement method ของ UITableViewDelegate และ UITableViewDataSource
เพิ่ม Code ใน MainViewController.m

#pragma mark -
#pragma mark UITableViewDelegate And UITableViewDataSource

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"entityCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"entityCell"] autorelease];
}
cell.textLabel.text = ((MyEntity *)[[self getData] objectAtIndex:indexPath.row]).name;
return cell;
}

-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section{
return [[self getData] count];
}



ต่อไปการลบข้อมูลนะครับ
ทำการเพิ่ม Method

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        NSManagedObject *entityToDelete = [[self getData] objectAtIndex:indexPath.row];
[managedObjectContext deleteObject:entityToDelete];
        NSError *error;
if (![managedObjectContext save:&error]) {
// Handle the error.
}
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
    }   
}



ใน Method addNew เพิ่ม [_tableView reloadData]; หลังจากเพิ่มข้อมูล

-(IBAction) addNew{
MyEntity *myEntity = (MyEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" 
inManagedObjectContext:managedObjectContext];
myEntity.name = _name.text;
NSError *error;
if (![managedObjectContext save:&error]) {
//Handle the error.
}
[_tableView reloadData]; // <<<< Add This Line.
}



เรื่องการ Update เพิ่ม Action สำหรับ Update ใน MainViewController.h

#import <UIKit/UIKit.h>
#import "MyEntity.h"

@interface MainViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> { 
IBOutlet UITextField *_name;
NSManagedObjectContext *managedObjectContext
UITableView *_tableView

NSIndexPath *_currentSelect; // <<<< Add This Line.
}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

-(IBAction) addNew;
-(IBAction) showData;

-(IBAction) update; // <<<< Add This Line.

@end




เพิ่มปุ่ม Update ใน Interface Builder โดยเปิด MainViewController.xib



เชื่อมปุ่ม Update กับ Action update (ctrl+ลาก)

ไปที่ MainViewController.m

เพิ่ม Method update และแก้ไข Code ที่ Method 
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

เพิ่ม Code 

-(IBAction) update{
NSManagedObject *selectObject = ((MyEntity *)[[self getData] objectAtIndex:_currentSelect.row]);
((MyEntity *)selectObject).name = _name.text;
NSError *error;
if (![managedObjectContext save:&error]) {
//Handle the error.
}
[_tableView reloadData];
}

-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
_currentSelect = indexPath;
_name.text = ((MyEntity *)[[self getData] objectAtIndex:indexPath.row]).name;
}



Build And Run

หลังจากเลือกข้อมูลใน Table จะสามารถแก้ไขข้อมูลโดยแก้ไขที่ช่องใส่ข้อมูล




วันศุกร์ที่ 12 พฤศจิกายน พ.ศ. 2553

CoreData Basic สร้าง Project With CoreData

สำหรับ Entry นี้จะเป็นการสร้าง Project ที่ต้องการใช้ CoreData จนถึงการเพิ่มข้อมูลนะครับ

เริ่มจากการสร้าง Project ใหม่ขึ้นมาก่อนครับ
ในหน้า New Project ตรงส่วนของ Product จะมี Use Core Data for storage ให้เลือกครับ



ทำการตั้งชื่อ Project 
ผมตั้งชื่อว่า CoreDataFromTemplate นะครับ
สร้าง Project ที่ใช้ Core Data เสร็จแล้ว

ใน Project จะมีไฟล์ CoreDataFromTemplate.xcdatamodel (ข้างหน้า . จะเป็นชื่อ Project นะครับ) 



เราจะทำการออกแบบโครงสร้างข้อมูลในนี้นะครับ
กดไปที่ไฟล์ CoreDataFromTemplate.xcdatamodel 

เริ่มจากการสร้าง Entity ก่อนนะครับ
กดเครื่องหมาย + ของ Entity หลังจากนั้นตั้งชื่อครับ (ตั้งชื่อ Class ด้วยนะครับ)



การใส่ Property เลือก Entity ที่ต้องการใส่ Property จาก Entity 
กดเครื่องหมาย + ในส่วนของ Property เลือก Add Attribute



ตั้งชื่อในช่อง Name และเลือกชนิดของข้อมูลที่ Type 



เราก็จะได้ Attribute ของ Entity แล้วครับ
เราสามารถเพิ่ม Attribute เข้าไปตามที่เราออกแบบโครงสร้างข้อมูลไว้
หลังจากนั้น Save ครับ

หลังจากออกแบบเสร็จเราจะทำการสร้าง Class ของข้อมูลที่เราออกแบบไว้
โดยคลิกขวาที่ Project เลือก Add -> New File…



เลือก Managed Object Class ครับ



เลือก Next ไปจนถึงหน้า Managed Object Class Generation
ทำการเลือก Entity ที่เราต้องการสร้าง Class กด Finish



เราจะได้ Class ของ Entity ที่เราต้องการสร้างขึ้นมา



(สำหรับบางทีที่ New File… แล้ว Managed Object Class ไม่ขึ้นให้เลือก
ให้ทำการเพิ่ม Entity ใน CoreDataFromTemplate.xcdatamodel แล้วลองเลือกใหม่
ปัญหานี้ผมยังหาวิธีแก้ที่มีประสิทธิภาพไม่ได้ครับ -"- ปัจจุบันใช้การสร้าง Entity ใหม่แล้ว
เลือกเฉพาะ Entity ที่ใช้งานครับ ถ้าได้วิธีที่ดีกว่านี้จะมาเพิ่มให้นะครับ หรือใครมีวิธีดีๆ 
ก็แนะนำกันได้ครับ)

การเพิ่มข้อมูล

สร้าง ViewController จากการ Add -> New File… เลือก UIViewController subclass
ตรง Option เลือก With XIB for user interface กด Next ตั้งชื่อ (ผมตั้งชื่อ MainViewController นะครับ)



จะได้ไฟล์เพิ่มขึ้น 3 ไฟล์



เริ่มจาก MainViewController.h
import Class ของข้อมูลที่เราออกแบบไว้
เพิ่ม NSManagedObjectContext และ Outlet ของ TextField
เพิ่ม Action สำหรับการกดใส่ข้อมูล

#import <UIKit/UIKit.h>
#import "MyEntity.h" // <<<< Add This Line

@interface MainViewController : UIViewController {
IBOutlet UITextField *_name; // <<<< Add This Line
NSManagedObjectContext *managedObjectContext; // <<<< Add This Line
}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; // <<<< Add This Line

-(IBAction) addNew; // <<<< Add This Line

@end



ใน MainViewController.xib
เพิ่มช่องใส่ข้อมูล และ ปุ่มกด
ทำการ Link Outlet (TextField) และ Action (Button) (กด Control แล้วลาก)



ใน MainViewController.m 
ทำการ synthesize managedObjectContext
และเพิ่ม Action addNew
Implement Action addNew

#import "MainViewController.h"


@implementation MainViewController

@synthesize managedObjectContext; // <<<< Add This Line

- (void)dealloc {
[_name release]; // <<<< Add This Line
[managedObjectContext release]; // <<<< Add This Line
    [super dealloc];
}


 // Add This Action
-(IBAction) addNew{
MyEntity *myEntity = (MyEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" 
inManagedObjectContext:managedObjectContext];
myEntity.name = _name.text;
NSError *error;
if (![managedObjectContext save:&error]) {
//Handle the error.
}
}

@end



กลับไปที่ CoreDataFromTemplateAppDelegate.h
ทำการ Import MainViewController.h

#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "MainViewController.h" // <<<< Add This Line



ใน CoreDataFromTemplateAppDelegate.h 
ที่ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
เพิ่ม Code

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

   // Begin Add Code.
NSManagedObjectContext *context = [self managedObjectContext];
if (!context) {
// Handle the error.
}
MainViewController *mainViewController = [[MainViewController alloc] init];
mainViewController.managedObjectContext = context;
[window addSubview:mainViewController.view];

// End Add Code.
    [window makeKeyAndVisible];

    return YES;
}



หลังจากนั้นลอง Build And Run

ใน Blog นี้จะถึงการ Add ข้อมูลนะครับ
ส่วนการอ่าน เขียน แก้ไข ลบ จะมาเขียนเพิ่มเติมให้ใน Blog หน้านะครับ

ส่วนใครที่ส่งสัยว่าข้อมูลจะถูกเก็บไว้จริงหรือเปล่าก็ให้ลองไปสร้าง Action สำหรับแสดงข้อมูล
ใน MainViewController ดูละกันครับ (สำหรับคนที่สร้างเป็น ส่วนที่ไม่เป็นรอ Entry หน้านะครับ)

Code : 
-(IBAction) showData{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyEntity" 
  inManagedObjectContext:managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
if (mutableFetchResults == nil) {
// Handle the error.
}
for (MyEntity *myEntity in mutableFetchResults) {
NSLog(@"%@", myEntity.name);
}
[mutableFetchResults release];
[request release];
}