前から疑問だったのですが、Core Data Programming Guideにやり方が書いてありました。ていうか先月の3月10日に追記されたっぽいです。
Scalar Valuesっていうところに書いてあります。
追記
コメントで @ishkawa@github さんに教えてもらいましたが、enumの場合はもっと簡単というか普通に実装する方法があるみたいでした。Core Data Programming Guideに書いてあるのは、doubleやCGRectを入れる場合の方法みたいですね。ishkawaさんありがとうございました!
何がしたいのか
たとえばこういう列挙型を定義したとして…。
typedef NS_ENUM(NSUInteger, kServiceType) {
kServiceTypeUnkown,
kServiceTypeTwitter,
kServiceTypeFacebook,
kServiceTypeTk,
};
こいつをCoreDataのオブジェクトで使いたくてInteger16とかの属性を作っても、アクセスする時のインターフェイスはNSNumberなのでどっかで変換したりしないといけなくてわずらわしい感じでした。ベストプラクティスってなんだろうと毎回疑問に思っていたのです。
公式のドキュメントに書いてあったやり方が簡単だったので、記録しておきます。CGRectとかも同じようにできるみたいでなかなか良い感じです。
モデルの属性
普通に数値を入れる場合と同じく Type Integer16 とかいう感じにします。たとえばservieTypeという名前にでもしておきましょう。
カスタムクラス
普通にNSManagedObjectを継承したクラスを作った上で、一部を変更します。クラスの外部にはenumで定義した列挙型を指定しておきます。
diff -up -ur old/SMService.h new/SMService.h
--- old/SMService.h 2014-04-08 16:46:44.000000000 +0900
+++ new/SMService.h 2014-04-08 16:46:29.000000000 +0900
@@ -6,5 +6,5 @@
@interface SMService : NSManagedObject
@property (nonatomic, retain) NSString * account;
-@property (nonatomic, retain) NSNumber * serviceType;
+@property (nonatomic) kServiceType serviceType;
@end
CoreDataが自動的にprimitiveなんとかっていうメソッドを作ってくれているので、それを使ってデータの読み書きをします。警告が出るので、カテゴリで宣言を追加しておきます。
diff -up -ur old/SMService.m new/SMService.m
--- old/SMService.m 2014-04-08 16:46:48.000000000 +0900
+++ new/SMService.m 2014-04-08 16:46:32.000000000 +0900
@@ -1,9 +1,30 @@
#import "SMService.h"
+@interface SMService (PrimitiveAccessors)
+@property (nonatomic) NSNumber *primitiveServiceType;
+@end
+
@implementation SMService
@dynamic account;
@dynamic serviceType;
+- (kServiceType)serviceType {
+ [self willAccessValueForKey:@"serviceType"];
+
+ NSNumber *tmpValue = [self primitiveServiceType];
+ [self didAccessValueForKey:@"serviceType"];
+
+ return (tmpValue != nil) ? [tmpValue intValue] : kServiceTypeUnkown;
+}
+
+- (void)setServiceType:(kServiceType)value {
+ NSNumber *temp = @(value);
+
+ [self willChangeValueForKey:@"serviceType"];
+ [self setPrimitiveServiceType:temp];
+ [self didChangeValueForKey:@"serviceType"];
+}
+
@end
たったこれだけの変更で直接enumの値を使えるようになりました。
SELECTする時のやり方は別に変わらないのかな。
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntity:[SMService class] context:context];
[request setPredicate:[NSPredicate predicateWithFormat:@"serviceType == %d", kServiceTypeTwitter]];
NSArray *objects = [context executeFetchRequest:request error:&error];