データベースの作成#
1. まず自分が何を必要としているかを分析する#
私の目的は、手紙のリスト画面をキャッシュすることです。新しいリスト画面のモデルは letter なので、Letter のエンティティが必要です。では、この Letter にはどんな属性があるのでしょうか?書き手 (sender)、手紙の内容 (content)、手紙の時間 (dateString)、手紙の既読未読状態 (isRead)、送信か受信か (incoming);なので、私の Letter のエンティティを作成した後はこのようになります:
これで十分でしょうか?いいえ、普通の表示画面であれば、letter のエンティティだけで十分ですが、私のこのプロジェクトは手紙を表示するもので、比較的プライベートなものです。私がキャッシュした手紙のリストは私だけが見るべきです。他の人が私の携帯電話にログインしたらどうなるでしょう?区別をしていないため、彼がログインすると、私のデータが見えてしまいます。データは携帯電話にキャッシュされているからです!
したがって、User のエンティティが必要です。この User のエンティティの目的は:Letter と結びつけて、各自が自分が見るべきものを見られるようにすることです;user には 2 つの属性があります:account と writeName(通常は uid ですが、私のは比較的簡単なので uid はありません);
次の問題は、Letter と User の間の関係です。一対一か一対多か、私の各 letter には一つの user が必要で、そして一つの user には多くの letter が必要です。データベースから letter を取得するとき、実際には user を使って検索しています。user と letter が一対一の場合、私は一通の手紙しか取得できませんが、それは明らかに間違っています。
結論:私のデータベースには 2 つのエンティティがあります。一つは Letter、一つは User;Letter と User の関係のタイプは一対一で、User と Letter の関係のタイプは一対多です;
2. 次にMagicalRecordをインポートする#
私は以前、データをキャッシュする際に FMDB を使用しており、CoreData は使ったことがありませんでしたが、MVC に完全に分割する際に、私はこのモデルを使って直接保存し、取り出すとモデルとして直接使用できることを望んでいました。属性を一つ一つ再設定するのではなく、CoreData を試してみたいと思いました。しかし、原生のものは複雑すぎるので、MagicalRecord を選びました。
MagicalRecord の使用:github には各メソッドの使用法しかありませんが、デモを見つけられなかったので、私がどのように使用したかを貼り付けます。参考までに。
pch ファイルにヘッダーファイルをインポートします。
a. applicationDidFinishLaunchingWithOptions: で初期化します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// アプリケーション起動後のカスタマイズのためのオーバーライドポイント。
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[MagicalRecord setupAutoMigratingCoreDataStack];
// windowのrootViewControllerを設定
[self setupWindowRootViewController];
return YES;
}
b. アプリケーションが停止する際に、cleanup メソッドを呼び出します。
- (void)applicationWillTerminate:(UIApplication *)application {
// アプリケーションが終了しようとしています。適切な場合はデータを保存します。applicationDidEnterBackground:も参照してください。
[MagicalRecord cleanUp];
}
c. モデルクラスを生成します。
.xcdatamodeld ファイルを選択し、CMD+N を押します。
すべて選択して、モデルクラスを生成します。
d. ネットワークリクエストが成功した後、データを保存します。
// letterをデータベースに保存
- (void)saveLetterWithLetterEntity:(LetterEntity *)tempLetter {
// MagicalRecordの保存メソッド、メインスレッドではない
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
// まず、pkNumberで検索してletterを取得
// letterが存在しない場合は作成
// 値を設定して保存、ここでのuser.lettersはNSSetであることに注意
Letter *letter = [Letter MR_findFirstByAttribute:@"pkNumber" withValue:tempLetter.pkNumber inContext:localContext];
if (!letter) {
// letterを作成
letter = [Letter MR_createEntityInContext:localContext];
User *user = [User MR_createEntityInContext:localContext];
letter.user = user;
}
letter.dateString = tempLetter.letterDateString;
letter.content = tempLetter.letterContent;
letter.sender = tempLetter.letterSender;
letter.pkNumber = tempLetter.pkNumber;
letter.incoming = [NSNumber numberWithBool:tempLetter.incoming];
// 受信はデフォルトで未読、送信はデフォルトで既読
if (tempLetter.incoming) {
// 受信
letter.isRead = [NSNumber numberWithBool:NO];
}
else {
letter.isRead = [NSNumber numberWithBool:YES];
}
letter.user.writeName = [[NSUserDefaults standardUserDefaults] objectForKey:k_WRITENAME];
letter.user.account = [[NSUserDefaults standardUserDefaults] objectForKey:k_USERNAME];
if (_letters == nil) {
_letters = [NSMutableArray array];
}
[_letters addObject:letter];
letter.user.letters = [NSSet setWithArray:_letters];
} completion:^(BOOL contextDidSave, NSError *error) {
DLog(@"=-===%@", (contextDidSave ? @"saveSuccessed" : @"saveFailure"));
}];
}
e. ネットワークが失敗した場合、データベースからデータを取得します。
- (NSMutableArray *)lettersFromDataBase {
NSMutableArray *receiveArray = [NSMutableArray array];
NSMutableArray *sendArray = [NSMutableArray array];
NSString *account = [[NSUserDefaults standardUserDefaults] objectForKey:k_USERNAME];
User *user = [[User MR_findByAttribute:@"account" withValue:account] firstObject];
// NSPredicate *receivePredicate = [NSPredicate predicateWithFormat:@"incoming == %@ && user == %@", [NSNumber numberWithBool:YES], user];
// NSPredicate *sendPredicate = [NSPredicate predicateWithFormat:@"incoming == %@ && user == %@", [NSNumber numberWithBool:NO], user];
// receiveArray = [NSMutableArray arrayWithArray:[Letter MR_findAllWithPredicate:receivePredicate]];
// sendArray = [NSMutableArray arrayWithArray:[Letter MR_findAllWithPredicate:sendPredicate]];
NSArray *userLetters = [Letter MR_findByAttribute:@"user" withValue:user];
if (userLetters) {
for (int i = 0; i < userLetters.count; i++) {
Letter *tempLetter = userLetters[i];
LetterEntity *tempEntity = [[LetterEntity alloc] init];
tempEntity.letterContent = tempLetter.content;
tempEntity.letterDateString = tempLetter.dateString;
tempEntity.letterSender = tempLetter.sender;
tempEntity.pkNumber = tempLetter.pkNumber;
tempEntity.incoming = [tempLetter.incoming boolValue];
if ([tempLetter.incoming boolValue]) {
[receiveArray addObject:tempEntity];
}
else {
[sendArray addObject:tempEntity];
}
}
}
// ここでの順序は間違ってはいけません。sendArrayが前、receiveArrayが後です。
NSMutableArray *resultArray = [NSMutableArray arrayWithObjects: sendArray, receiveArray, nil];
return resultArray;
}
参考#
-
深入浅出 MagicalRecord、このブログは非常に詳細で、CoreData から MagicalRecord まで説明しています。
-
RayWenderlich の MagicalRecord Tutorial、このチュートリアルは、実際に打ち込んで練習できます。