iOSのコアデータ。 章番号1。 実用部

ハブラリュディ、こんにちは!
本日、マイケル・プリバートとロバート・ワーナーの本「iOS用のプロコアデータ」に関する実践的な演習で一連の講義を書き始めたいと思います。 各章には、理論的および実用的な部分が含まれます。



内容:




実用部


これは最初の章であり、最初の章と見なすことができるため、実用的なタスクとして、VKの友人のリストを表示し、Core Dataを使用してそれらに関するデータを保存する通常のソーシャルアプリケーションを作成することを選択します。
およそ(プロセスで追加/除外するものを決定します)数時間(または数分)のハードプログラミングの後、アプリケーションは次のようになります。
画像
画像


想像のとおりVkontakte iOS SDK v2.0を使用します
ちなみに、実際にはXcodeだけでなくAppCodeも使用されることをおaびします(製品のJBの皆さんに感謝します!)。 AppCodeでできることはすべてそこで行われます。

行こう...

空のプロジェクトを作成する

コアデータなしの空のプロジェクト-シングルビューアプリケーションを作成します。
画像
画像

アプリケーションが正常に開始されました:
画像

UITableViewの追加とカスタマイズ

ASAViewController.hを開き、次のプロパティを追加します。
@property (nonatomic, strong) UITableView *tableView; 

ASAViewController.hの完全なビュー:
 #import <UIKit/UIKit.h> @interface ASAViewController : UIViewController @property (nonatomic, strong) UITableView *tableView; @end 

ASAViewController.mを開き、UITableViewテーブルを作成するための行をviewDidLoadメソッドに追加します。
  CGRect frame = [[UIScreen mainScreen] bounds]; _tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain]; [self.view addSubview:_tableView]; 

ASAViewController.mの完全なビュー:
 #import "ASAViewController.h" @implementation ASAViewController - (void)viewDidLoad { CGRect frame = [[UIScreen mainScreen] bounds]; _tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain]; [_tableView setDelegate:self]; [_tableView setDataSource:self]; [self.view addSubview:_tableView]; } @end 


以下を開始します。
画像

デリゲートメソッドUITableViewDelegateおよびUITableViewDataSourceを実装することは残っています。
ASAViewController.hにプロトコルを追加します。
 @interface ASAViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> 

ASAViewController.mを開いて、2つのメソッドを実装します(1つはリスト内の友人の数を返すため、もう1つはユーザーデータで塗りつぶされたセルを作成するため):
 #pragma mark - UITableViewDelegate & UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [_userFriends count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"friendID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; if(nil == cell){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; } // setting default image while main photo is loading cell.imageView.image = [UIImage imageNamed:@"default.png"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSString* imgPath = _userFriends[(NSUInteger)indexPath.row][@"photo"]; NSData* img = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath]]; dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = [UIImage imageWithData:img]; }); }); NSString* firstName = _userFriends[(NSUInteger)indexPath.row][@"first_name"]; NSString* lastName = _userFriends[(NSUInteger)indexPath.row][@"last_name"]; NSString* fullName = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; cell.textLabel.text = fullName; NSString* status = _userFriends[(NSUInteger)indexPath.row][@"status"]; cell.detailTextLabel.text = status; return cell; } 


_userFriends変数は、ASAViewControllerのプロパティです。
 @property (nonatomic, strong) NSMutableArray *userFriends; 


ASAViewController.hおよびASAViewController.mの最終ビュー:
 #import <UIKit/UIKit.h> @interface ASAViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, strong) UITableView *tableView; @property (nonatomic, strong) NSMutableArray *userFriends; @end 

 #import "ASAViewController.h" @implementation ASAViewController - (void)viewDidLoad { _userFriends = [[NSMutableArray alloc] init]; CGRect frame = [[UIScreen mainScreen] bounds]; _tableView = [[UITableView alloc] initWithFrame:frame style:UITableViewStylePlain]; [_tableView setDelegate:self]; [_tableView setDataSource:self]; [self.view addSubview:_tableView]; } #pragma mark - UITableViewDelegate & UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [_userFriends count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"friendID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; if(nil == cell){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; } // setting default image while main photo is loading cell.imageView.image = [UIImage imageNamed:@"default.png"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSString* imgPath = _userFriends[(NSUInteger)indexPath.row][@"photo"]; NSData* img = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath]]; dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = [UIImage imageWithData:img]; }); }); NSString* firstName = _userFriends[(NSUInteger)indexPath.row][@"first_name"]; NSString* lastName = _userFriends[(NSUInteger)indexPath.row][@"last_name"]; NSString* fullName = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; cell.textLabel.text = fullName; NSString* status = _userFriends[(NSUInteger)indexPath.row][@"status"]; cell.detailTextLabel.text = status; return cell; } @end 

すべてがバタンと始まるはずです。 次のステップに進みます。

統合VKontakte iOS SDK v2.0

このリンクのソースを取得します。

QuartzCore.frameworkの接続
画像

Vkontakte iOS SDKを追加する
画像

ASAAppDelegate.hで、2つのプロトコルを追加します。
 @interface ASAAppDelegate : UIResponder <UIApplicationDelegate, VKConnectorDelegate, VKRequestDelegate> 


ASAAppDelegate.m実装ファイルを開き、次の行を- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptionsメソッドに貼り付けます- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
  [[VKConnector sharedInstance] setDelegate:self]; [[VKConnector sharedInstance] startWithAppID:@"3541027" permissons:@[@"friends"]]; 

このコードは、アプリケーションの起動時に、ソーシャルネットワークVKontakteでの承認のためのポップアップウィンドウをユーザーに表示します。
画像

ASAAppDelegate.mでは、さらに2つのメソッドを実装します。
 #pragma mark - VKConnectorDelegate - (void) VKConnector:(VKConnector *)connector accessTokenRenewalSucceeded:(VKAccessToken *)accessToken { // now we can make request [[VKUser currentUser] setDelegate:self]; [[VKUser currentUser] friendsGet:@{ @"uid" : @([VKUser currentUser].accessToken.userID), @"fields" : @"first_name,last_name,photo,status" }]; } #pragma mark - VKRequestDelegate - (void)VKRequest:(VKRequest *)request response:(id)response { ASAViewController *controller = (ASAViewController *)self.window.rootViewController; controller.userFriends = response[@"response"]; [controller.tableView reloadData]; } 

この段階でのASAAppDelegate.hおよびASAAppDelegate.mの最終ビュー:
 #import <UIKit/UIKit.h> #import "VKConnector.h" #import "VKRequest.h" @class ASAViewController; @interface ASAAppDelegate : UIResponder <UIApplicationDelegate, VKConnectorDelegate, VKRequestDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) ASAViewController *viewController; @end 

 #import "ASAAppDelegate.h" #import "ASAViewController.h" #import "VKUser.h" #import "VKAccessToken.h" @implementation ASAAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.viewController = [[ASAViewController alloc] initWithNibName:@"ASAViewController" bundle:nil]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; [[VKConnector sharedInstance] setDelegate:self]; [[VKConnector sharedInstance] startWithAppID:@"3541027" permissons:@[@"friends"]]; return YES; } #pragma mark - VKConnectorDelegate - (void) VKConnector:(VKConnector *)connector accessTokenRenewalSucceeded:(VKAccessToken *)accessToken { // now we can make request [[VKUser currentUser] setDelegate:self]; [[VKUser currentUser] friendsGet:@{ @"uid" : @([VKUser currentUser].accessToken.userID), @"fields" : @"first_name,last_name,photo,status" }]; } #pragma mark - VKRequestDelegate - (void)VKRequest:(VKRequest *)request response:(id)response { ASAViewController *controller = (ASAViewController *)self.window.rootViewController; controller.userFriends = response[@"response"]; [controller.tableView reloadData]; } @end 

アプリケーションを起動すると、次のようなものが表示されます(上記の例では、クエリキャッシュが意図的に使用されていないことを忘れないでください)。
画像
画像

コアデータからのデザート

だから、私たちは最も興味深く、魅力的なものに来ました! 実用的な部分を完了したいというあなたの欲求を失っていないことを願っています;)気を散らし、ドライヤーでお茶を飲み、キャンディーをかじり、足を伸ばし、足を伸ばします。

ここでコアデータが必要なのはなぜですか? VKontakteサーバーへの最初の要求に応じて、友人のリストと要求されたフィールド(ステータス、写真、名、姓)を受け取り、この情報をCore Dataを使用してローカルストレージに保存し、要求と表示中にアプリケーションを起動してインターネットをオフにします最初のリクエスト中にローカルに保存されたユーザーの友人のリスト。 来ますか? それでは始めましょう。

インターネット接続がないという事実を処理するために、VKRequestDelegateプロトコルの次のメソッドを使用します。
 - (void)VKRequest:(VKRequest *)request connectionErrorOccured:(NSError *)error { // TODO } 

メソッドの本体は少し後で記述します。

そうそう、私は完全に忘れてしまった! CoreData.frameworkを接続しCoreData.framework
画像
ASAAppDelegate.hに3つのお気に入りのプロパティを追加します。
 @property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; @property (nonatomic, strong) NSPersistentStoreCoordinator *coordinator; @property (nonatomic, strong) NSManagedObjectContext *managedObjectContext; 


次に、3つのプロパティすべてに対して明示的なゲッターを実装するために、ASAAppDelegate.mに移動します。
管理オブジェクトモデル:
 - (NSManagedObjectModel *)managedObjectModel { if(nil != _managedObjectModel) return _managedObjectModel; _managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil]; return _managedObjectModel; } 

永続ストアコーディネーター:
 - (NSPersistentStoreCoordinator *)coordinator { if(nil != _coordinator) return _coordinator; NSURL *storeURL = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"BasicApplication.sqlite"]; _coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; NSError *error = nil; if(![_coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){ NSLog(@"Unresolved error %@, %@", error, [error userInfo]); abort(); } return _coordinator; } 

管理対象オブジェクトのコンテキスト:
 - (NSManagedObjectContext *)managedObjectContext { if(nil != _managedObjectContext) return _managedObjectContext; NSPersistentStoreCoordinator *storeCoordinator = self.coordinator; if(nil != storeCoordinator){ _managedObjectContext = [[NSManagedObjectContext alloc] init]; [_managedObjectContext setPersistentStoreCoordinator:storeCoordinator]; } return _managedObjectContext; } 


ビルド...そして...そして...すべてが順調です。

次に、モデルの作成に進みます。 ところで、私は保険なしですべてを行い、最終的には何かが何かに適合しないかもしれないことに注意したいと思いますが、私たちは勇敢なプログラマーです!
モデルを作成するには、同じXcodeが必要です。
プロジェクトを開き、Control + Nを押して、コアデータ->データモデルを選択します。
画像

モデルをFriendとして保存します。
画像

かなり馴染みのある画面が表示されます。
画像

Friendという新しいエンティティを作成し、last_name(String)、first_name(String)、status(String)、photo(Binary Data)の4つのプロパティを追加します。
画像

Xcodeを終了して閉じます。

次に行う必要があるのは、リクエストの完了後にユーザーデータを保存することです。
ASAAppDelegate.mを開き、VKRequest:response:メソッドに移動して、次のように変更します。
 - (void)VKRequest:(VKRequest *)request response:(id)response { ASAViewController *controller = (ASAViewController *)self.window.rootViewController; controller.userFriends = response[@"response"]; [controller.tableView reloadData]; //    ,     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ for(NSDictionary *user in controller.userFriends){ NSManagedObject *friend = [NSEntityDescription insertNewObjectForEntityForName:@"Friend" inManagedObjectContext:self.managedObjectContext]; [friend setValue:user[@"first_name"] forKey:@"first_name"]; [friend setValue:user[@"last_name"] forKey:@"last_name"]; [friend setValue:[NSData dataWithContentsOfURL:[NSURL URLWithString:user[@"photo"]]] forKey:@"photo"]; [friend setValue:user[@"status"] forKey:@"status"]; NSLog(@"friend: %@", friend); } if([self.managedObjectContext hasChanges] && ![self.managedObjectContext save:nil]){ NSLog(@"Unresolved error!"); abort(); } }); } 

各反復で、新しいオブジェクトを作成し、フィールドを設定して保存します。 コンソールでは、目を楽しませるラインを見ることができます。
画像

税金は、インターネット接続が切断されたときにテーブルの表示を改善するために残っています。 すべてのコードは- (void)VKRequest:(VKRequest *)request connectionErrorOccured:(NSError *)errorメソッドに移動し、 - (void)VKRequest:(VKRequest *)request connectionErrorOccured:(NSError *)errorようになります。
 - (void)VKRequest:(VKRequest *)request connectionErrorOccured:(NSError *)error { //         NSMutableArray *data = [[NSMutableArray alloc] init]; //      NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Friend"]; NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"last_name" ascending:YES]; [fetchRequest setSortDescriptors:@[sortDescriptor]]; //   NSArray *tmpData = [self.managedObjectContext executeFetchRequest:fetchRequest error:nil]; //   for(NSManagedObject *object in tmpData){ //    ,         -  :) if([object valueForKey:@"status"] == nil) continue; NSDictionary *tmp = @{ @"last_name": [object valueForKey:@"first_name"], @"first_name": [object valueForKey:@"last_name"], @"photo": [object valueForKey:@"photo"], @"status": [object valueForKey:@"status"] }; [data addObject:tmp]; } //   ""    ASAViewController *controller = (ASAViewController *)self.window.rootViewController; controller.userFriends = data; [controller.tableView reloadData]; } 


そして、メソッドを少し調整する必要があります- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"friendID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID]; if(nil == cell){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellID]; } // setting default image while main photo is loading cell.imageView.image = [UIImage imageNamed:@"default.png"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ NSData *img; if([_userFriends[(NSUInteger) indexPath.row][@"photo"] isKindOfClass:[NSData class]]){ img = _userFriends[(NSUInteger) indexPath.row][@"photo"]; } else { NSString* imgPath = _userFriends[(NSUInteger)indexPath.row][@"photo"]; img = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath]]; } dispatch_async(dispatch_get_main_queue(), ^{ cell.imageView.image = [UIImage imageWithData:img]; }); }); NSString* firstName = _userFriends[(NSUInteger)indexPath.row][@"first_name"]; NSString* lastName = _userFriends[(NSUInteger)indexPath.row][@"last_name"]; NSString* fullName = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; cell.textLabel.text = fullName; NSString* status = _userFriends[(NSUInteger)indexPath.row][@"status"]; cell.detailTextLabel.text = status; return cell; } 


やった! アプリケーションが完了し、ローカルストレージの友人が表示されます。
画像

喜びの涙

最後に、最後の実用的な部分ではなく、最初の部分を終了しました。 このリンクでプロジェクト全体を見つけることができます(アーカイブ内にあります)。

背中と指が疲れないことを願っています。
Core Dataでの時間に満足していただければ幸いです。
続編を見たいと思います。

ご注意

批評であっても、コメントが残されているように、著者を満足させるものはありません;)

ご清聴ありがとうございました!

Source: https://habr.com/ru/post/J191472/


All Articles