24/7 twenty-four seven

iOS/OS X application programing topics.

CoreData の NSManagedObject のサブクラスを変更する場合はカテゴリを使うと便利

CoreDataのモデルクラスはXcodeのモデルエディタから自動生成しますが、生成されたクラスにメソッドを追加したりしたいことがあると思います。
そのとき、自動生成されたファイルを直接変更してしまうと、モデルに変更がありモデルクラスを再生成したときにその変更が上書きされてしまいます。

そこで、カテゴリを使って追加部分は別のファイルに分けておくと、モデルクラスを再生成しても後から追加した部分は上書きされずに残るのでそのまま使えます。


例えば下記のようなクラス (Event.h) があるとして、条件でフェッチするメソッドや、日付をフォーマットして返すメソッドを Event+CoreData.h/m や Event+Formatter.h/m として別ファイルに定義します。

// Event.h

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@class Favorites, History, User;

@interface Event : NSManagedObject

@property (nonatomic, retain) NSNumber * accepted;
@property (nonatomic, retain) NSString * address;
@property (nonatomic, retain) NSString * catch;
@property (nonatomic, retain) NSDate * endedAt;
@property (nonatomic, retain) NSString * eventDescription;
@property (nonatomic, retain) NSNumber * eventID;
@property (nonatomic, retain) NSString * eventURL;
@property (nonatomic, retain) NSNumber * favorite;
〜(略)〜
// Event+CoreData.h

#import <Foundation/Foundation.h>
#import "Event.h"

@interface Event(CoreData)

+ (Event *)eventWithEventID:(NSNumber *)eventID;
+ (NSArray *)eventsWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate;

@end
// Event+CoreData.m

#import "Event+CoreData.h"
#import "EPCoreDataManager.h"

@implementation Event(CoreData)

+ (Event *)eventWithEventID:(NSNumber *)eventID {
    EPCoreDataManager *manager = [EPCoreDataManager sharedManager];
    NSManagedObjectContext *context = manager.managedObjectContext;
    
    NSPredicate *predicate = [self predicateWithEventID:eventID];
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.predicate = predicate;
    
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:context];
    request.entity = entity;
    
    NSArray *results = [context executeFetchRequest:request error:nil];
    
    Event *event = [results lastObject];
    if (!event) {
        event = [[Event alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
        event.eventID = eventID;
    }
    
    return event;
}

+ (NSArray *)eventsWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate {
    EPCoreDataManager *manager = [EPCoreDataManager sharedManager];
    NSManagedObjectContext *context = manager.managedObjectContext;
    
    NSPredicate *predicate = [Event predicateWithStartDate:startDate endDate:endDate];
    
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    request.predicate = predicate;
    
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"startedAt" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
    
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:context];
    request.entity = entity;
    
    NSArray *results = [context executeFetchRequest:request error:nil];
    
    return results;
}

@end
// Event+Formatter.h

#import <Foundation/Foundation.h>
#import "Event.h"

@interface Event(Formatter)

- (NSString *)formattedStartedAt;
- (NSString *)formattedEndedAt;
- (NSString *)formattedUpdatedAt;

@end
// Event+Formatter.m

#import "Event+Formatter.h"

@implementation Event(Formatter)

- (NSString *)formattedStartedAt {
    return [[self dateFormatter] stringFromDate:self.startedAt];
}

- (NSString *)formattedEndedAt {
    return [[self dateFormatter] stringFromDate:self.endedAt];
}

- (NSString *)formattedUpdatedAt {
    return [[self dateFormatter] stringFromDate:self.updatedAt];
}

- (NSDateFormatter *)dateFormatter {
    static NSDateFormatter *dateFormatter;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        dateFormatter = [[NSDateFormatter alloc] init];
        NSLocale *locale = [NSLocale currentLocale];
        NSArray *preferredLanguages = [NSLocale preferredLanguages];
        if ([preferredLanguages count] > 0) {
            locale = [[NSLocale alloc] initWithLocaleIdentifier:[preferredLanguages objectAtIndex:0]];
        }
        dateFormatter.locale = locale;
        dateFormatter.dateStyle = NSDateFormatterLongStyle;
        dateFormatter.timeStyle = NSDateFormatterShortStyle;
    });
    
    return dateFormatter;
}

@end