2010年11月8日月曜日

View-based Application で CoreData を使えるようしたい

新規プロジェクト作成のテンプレートでView-based Applicationを選択してプロジェクトを作成する。Core Dataを使いたいがどうすればいいのか。
Navigation-based Applicationテンプレートを選ぶ場合はCore Dataを使うかどうか選択できるようになってるけど、View-basedではチェックボックスが出てこない。
それならば自分で実装しようということになるのだけれど、うーんうーんって感じになる場合が多いと思うの。

なので手順を書いておく。

Xcode 3.2.4 and iOS SDK 4.1 で試してる。
古いバージョンとか新しいバージョンのこと考えてない。

新規プロジェクトをView-based Applicationを選択して名前をMyCDataとして作成してます。


僕がやったポイント。

  1. CoreData.frameworkを追加
  2. DataModel(.xcdatamodel)ファイルを新規作成
  3. DataModelにエンティティ作ってプロパティを追加
  4. .xcdatamodelを.xcdatamodeldにバンドル
  5. Managed Object Class(NSManagedObjectサブクラス)ファイルを新規作成
  6. おまじないのコード(NSManagedObjectContextなどを用意)
  7. 後は普通にコード書く
  8. データの読み込みや追加などは、用意したコンテクスト(NSManagedObjectContext)を通じて実行



1.Frameworksグループを右クリック>追加>既存のフレームワーク>CoreData.frameworkを追加

2.Resourcesグループを右クリック>追加>新規ファイル>DataModelを選択>MyCData.xcdatamodelと名前付けて次へでそのまま完了。

3.MyCData.xcdatamodelにエンティティPersonを追加して、そのプロパティにageとnameを追加。

データ型を
age は Int16
name は String
とした。

4.MyCData.xcdatamodelを選択した状態で、設計>データモデル>モデルバージョンを追加。

MyCData.xcdatamodeldにMyCData.xcdatamodelがバンドルされた形になる。
MyCData.xcdatamodeldの中にxcdatamodelファイルが1つ追加されるけどいらないので削除する。

※この4.をやらないで対応する方法もあるけれどそれは書かない。将来的にモデルの変更(プロパティの追加など)がされる可能性を考えれならこれやっとけばいいじゃん、必要になるでしょってことで。


5.エンティティPersonを選択した状態でファイル追加をするとManaged Object Classが選べるので、そのまま完了するとPerson.h/.mが追加される。
  1. //  
  2. //  Person.h  
  3. //  
  4. #import <CoreData/CoreData.h>  
  5.   
  6.   
  7. @interface Person :  NSManagedObject    
  8. {  
  9. }  
  10.   
  11. @property (nonatomic, retain) NSNumber * age;  
  12. @property (nonatomic, retain) NSString * name;  
  13.   
  14. @end  

  1. //   
  2. //  Person.m  
  3. //  
  4.   
  5. #import "Person.h"  
  6.   
  7.   
  8. @implementation Person   
  9.   
  10. @dynamic age;  
  11. @dynamic name;  
  12.   
  13. @end  



6.おまじないコード

  1. //  
  2. //  MyCDataAppDelegate.h  
  3. //  
  4. #import <UIKit/UIKit.h>  
  5.   
  6. // ここ追加  
  7. #import <CoreData/CoreData.h>  
  8.   
  9. @class MyCDataViewController;  
  10.   
  11. @interface MyCDataAppDelegate : NSObject <UIApplicationDelegate> {  
  12.     UIWindow *window;  
  13.     MyCDataViewController *viewController;  
  14. // ここ追加  
  15. @private  
  16.     NSManagedObjectContext *managedObjectContext_;  
  17.     NSManagedObjectModel *managedObjectModel_;  
  18.     NSPersistentStoreCoordinator *persistentStoreCoordinator_;  
  19. }  
  20.   
  21. @property (nonatomic, retain) IBOutlet UIWindow *window;  
  22. @property (nonatomic, retain) IBOutlet MyCDataViewController *viewController;  
  23.   
  24. // ここ追加  
  25. @property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;  
  26. @property (nonatomic, retain, readonly) NSManagedObjectModel *managedObjectModel;  
  27. @property (nonatomic, retain, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;  
  28.   
  29. // ここ追加  
  30. - (NSString *)applicationDocumentsDirectory;  
  31.   
  32. @end  

  1. //  
  2. //  MyCDataAppDelegate.m  
  3. //  
  4. #import "MyCDataAppDelegate.h"  
  5. #import "MyCDataViewController.h"  
  6.   
  7. @implementation MyCDataAppDelegate  
  8.   
  9. @synthesize window;  
  10. @synthesize viewController;  
  11.   
  12.   
  13. #pragma mark -  
  14. #pragma mark Application lifecycle  
  15.   
  16. /** 
  17.  これ追加 
  18.  */  
  19. - (void)awakeFromNib {      
  20.     viewController.managedObjectContext = self.managedObjectContext;  
  21. }  
  22.   
  23. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {      
  24.     [window addSubview:viewController.view];  
  25.     [window makeKeyAndVisible];  
  26.     return YES;  
  27. }  
  28.   
  29. - (void)applicationWillResignActive:(UIApplication *)application {  
  30. }  
  31.   
  32. - (void)applicationDidEnterBackground:(UIApplication *)application {  
  33. }  
  34.   
  35. - (void)applicationWillEnterForeground:(UIApplication *)application {  
  36. }  
  37.   
  38. - (void)applicationDidBecomeActive:(UIApplication *)application {  
  39. }  
  40.   
  41. - (void)applicationWillTerminate:(UIApplication *)application {  
  42. }  
  43.   
  44.   
  45. #pragma mark -  
  46. #pragma mark Memory management  
  47.   
  48. - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {  
  49. }  
  50.   
  51. - (void)dealloc {  
  52.     [viewController release];  
  53.     [window release];  
  54.     [super dealloc];  
  55. }  
  56.   
  57.   
  58. #pragma mark -  
  59. #pragma mark Core Data stack  
  60.   
  61. /** 
  62.  これ追加(ゲッター) 
  63.  CoreData用に共通のContextを1つここで用意する。 
  64.  SQLiteファイルへの永続ストアコーディネータ(NSPersistentStoreCoordinator)のインスタンスを、用意したContextにセットする。 
  65.  */  
  66. - (NSManagedObjectContext *)managedObjectContext {  
  67.       
  68.     if (managedObjectContext_ != nil) {  
  69.         return managedObjectContext_;  
  70.     }  
  71.       
  72.     NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];  
  73.     if (coordinator != nil) {  
  74.         managedObjectContext_ = [[NSManagedObjectContext alloc] init];  
  75.         [managedObjectContext_ setPersistentStoreCoordinator:coordinator];  
  76.     }  
  77.     return managedObjectContext_;  
  78. }  
  79.   
  80.   
  81. /** 
  82.  これ追加(ゲッター) 
  83.  .xcdatamodeld (.xcdatamodelを内包した)バンドルを指定する。 
  84.  管理オブジェクトモデル(NSManagedObjectModel)の準備をする。 
  85.  この管理オブジェクトモデルを使うことで、管理オブジェクト(アプリ側)とレコード(データベース側)とのマッピングさせる。 
  86.  */  
  87. - (NSManagedObjectModel *)managedObjectModel {  
  88.       
  89.     if (managedObjectModel_ != nil) {  
  90.         return managedObjectModel_;  
  91.     }  
  92.       
  93.     NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"MyCData" ofType:@"momd"];  
  94.     NSLog(@"データモデルのバンドルディレクトリ(?)のパス %@", modelPath);  
  95.     NSURL *modelURL = [NSURL fileURLWithPath:modelPath];  
  96.     managedObjectModel_ = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];      
  97.       
  98.     return managedObjectModel_;  
  99. }  
  100.   
  101. /** 
  102.  これ追加(ゲッター) 
  103.  永続ストアとしてSQLite(ファイル)を指定する。 
  104.  この永続ストアコーディネータを使うことで、管理オブジェクトモデル(.xcdatamodel)と永続ストアを関連付けて管理できるようになる。 
  105.  */  
  106. - (NSPersistentStoreCoordinator *)persistentStoreCoordinator {  
  107.       
  108.     if (persistentStoreCoordinator_ != nil) {  
  109.         return persistentStoreCoordinator_;  
  110.     }  
  111.       
  112.     NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"MyCData.sqlite"]];  
  113.       
  114.       
  115.     NSError *error = nil;  
  116.     persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];  
  117.       
  118.     if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {  
  119.         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);  
  120.         abort();  
  121.     }      
  122.       
  123.     return persistentStoreCoordinator_;  
  124. }  
  125.   
  126.   
  127. #pragma mark -  
  128. #pragma mark Application's Documents directory  
  129.   
  130. /** 
  131.  これ追加 
  132.  Returns the path to the application's Documents directory. 
  133.  */  
  134. - (NSString *)applicationDocumentsDirectory {  
  135.     return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];  
  136. }  
  137.   
  138.   
  139. @end  

7.後は普通にコード書く
8.データの読み込みや追加などは、用意したコンテクスト(NSManagedObjectContext)を通じて実行
  1. //  
  2. //  MyCDataViewController.h  
  3. //  
  4. #import <UIKit/UIKit.h>  
  5.   
  6. // ここ追加  
  7. #import <CoreData/CoreData.h>  
  8.   
  9. @interface MyCDataViewController : UIViewController {  
  10.     // ここ追加  
  11.     NSFetchedResultsController *fetchedResultsController_;  
  12.     NSManagedObjectContext *managedObjectContext_;  
  13.   
  14.     // ここ追加  
  15.     IBOutlet UIButton *addButton;  
  16.     IBOutlet UIButton *requestButton;  
  17.     NSMutableArray *persons;  
  18. }  
  19.   
  20.   
  21. // ここ追加  
  22. @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;  
  23. @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController;  
  24.   
  25. // ここ追加  
  26. @property (nonatomic, retain) IBOutlet UIButton *addButton;  
  27. @property (nonatomic, retain) IBOutlet UIButton *requestButton;  
  28. @property (nonatomic, retain) NSMutableArray *persons;  
  29.   
  30. - (IBAction)addPerson:(id)sender;  
  31. - (IBAction)requestPersons:(id)sender;  
  32.   
  33. @end  

  1. //  
  2. //  MyCDataViewController.m  
  3. //  
  4. #import "MyCDataViewController.h"  
  5.   
  6. // ここ追加  
  7. #import "Person.h"  
  8.   
  9. @implementation MyCDataViewController  
  10.   
  11. // ここ追加  
  12. @synthesize fetchedResultsController = fetchedResultsController_;  
  13. @synthesize managedObjectContext = managedObjectContext_;  
  14. @synthesize addButton;  
  15. @synthesize requestButton;  
  16. @synthesize persons;  
  17.   
  18. - (void)didReceiveMemoryWarning {  
  19.     [super didReceiveMemoryWarning];  
  20. }  
  21.   
  22. - (void)viewDidUnload {  
  23.     // ここ追加  
  24.     self.addButton = nil;  
  25.     self.requestButton = nil;  
  26. }  
  27.   
  28. - (void)dealloc {  
  29.     // ここ追加  
  30.     [addButton release];  
  31.     [requestButton release];  
  32.     [persons release];  
  33.       
  34.     // ここ追加  
  35.     [fetchedResultsController_ release];  
  36.     [managedObjectContext_ release];  
  37.   
  38.     [super dealloc];  
  39. }  
  40.   
  41.   
  42. /** 
  43.  これ追加 
  44.  */  
  45. - (IBAction)addPerson:(id)sender  
  46. {  
  47.     NSLog(@"- (IBAction)addPerson:(id)sender");  
  48.     NSManagedObjectContext *context = self.managedObjectContext;  
  49.       
  50.     Person *person = (Person *)[NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:context];  
  51.       
  52.     [person setAge:[NSNumber numberWithInt:37]];  
  53.     [person setName:@"Jack"];  
  54.       
  55.     NSError *error;  
  56.     if (![context save:&error]) {  
  57.         NSLog(@"Error");  
  58.     }  
  59. }  
  60.   
  61. /** 
  62.  これ追加 
  63.  */  
  64. - (IBAction)requestPersons:(id)sender  
  65. {  
  66.     NSLog(@"- (IBAction)requestPersons:(id)sender");  
  67.     NSManagedObjectContext *context = self.managedObjectContext;  
  68.       
  69.     NSFetchRequest *request = [[NSFetchRequest alloc] init];  
  70.     NSEntityDescription *entity = [NSEntityDescription entityForName:@"Person" inManagedObjectContext:context];  
  71.     [request setEntity:entity];  
  72.       
  73.     // ソート  
  74.     NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];  
  75.     NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];  
  76.     [request setSortDescriptors:sortDescriptors];  
  77.     [sortDescriptor release];  
  78.     [sortDescriptors release];  
  79.   
  80.       
  81.     NSError *error;  
  82.     NSMutableArray *mutableFetchResults = [[context executeFetchRequest:request error:&error] mutableCopy];  
  83.     if (mutableFetchResults == nil) {  
  84.         NSLog(@"no results");  
  85.     }  
  86.       
  87.     [self setPersons:mutableFetchResults];  
  88.     [mutableFetchResults release];  
  89.     [request release];  
  90.   
  91.   
  92.     for (Person *person in persons) {  
  93.         NSLog(@"%@", [person name]);  
  94.     }  
  95. }  
  96.   
  97. @end  

後このサンプルでは、InterfaceBuilderでMyCDataViewController.xibにボタン2つ追加してそれぞれIBOutletとIBActionを繋げれば動くはず。

0 件のコメント:

コメントを投稿