コアデータ:最小限のコードでデータをインポートする

多くの開発者と同じように、特に不必要と思われる場合には、多くのコードを書くことはあまり好きではありません。初期段階では、このコードを最適化および一般化する方法を見つけようとします。 Core Data自体に関しては、これらの無限のフェッチと新しいオブジェクトの作成はすべて簡単にできるといつも思っていました。 次に、ハブでよく言及されるActiveRecordパターンと、Objective-C- MagicalRecordでの (私の意見では)非常に優れた実装を発見しました 。 私は説明を掘り下げません-すべてがプロジェクトページで説明されている非常にアクセスしやすいです。

簡素化の次のステップは、外部からのデータをマップすることでした。



私自身は、額でこの問題を解決し、長い間そのように機能しました。 各ManagedObject継承には、着信JSONディクショナリを解析する次のメソッドが含まれていました。



- (void)mapPropertiesFrom:(NSDictionary *)dictonary { self.identifier = [NSNumber numberWithInt:[[dictonary objectForKey:@"id"] intValue]]; self.privacy = [NSNumber numberWithInt:[[dictonary objectForKey:@"public"] intValue]]; self.author = [KWUser findFirstByAttribute:@"profileId" withValue:[dictonary objectForKey:@"profile"]]; self.profileId = [NSNumber numberWithInt:[[dictonary objectForKey:@"profile"] intValue]]; self.latitude = [NSNumber numberWithDouble:[[dictonary objectForKey:@"lat"] doubleValue]]; self.longitude = [NSNumber numberWithDouble:[[dictonary objectForKey:@"lon"] doubleValue]]; self.text = [dictonary objectForKey:@"text"]; self.category = [dictonary objectForKey:@"category"]; self.firstName = [dictonary objectForKey:@"firstname"]; self.lastName = [dictonary objectForKey:@"lastname"]; }
      
      







明らかに、そのようなオブジェクトはたくさんあり、その上、それらには関係もあります。 最初に思い浮かぶのは、実行時に属性と関係を整理し、結果をオブジェクトに保存することです。 残念ながら 、喜び、私は車輪を再発明するのが好きではなく、最近、このアプローチの非常に興味深い実装に出会いました。



魔法のインポート



MagicalImportは、MagicalRecordの機能を拡張し、コアデータでJSONオブジェクトのマッピングを直接構成できるカテゴリのセットであり、コードの記述は最小限に抑えられます。 開発者が自分で設定した実装と目標の詳細については掘り下げませんが、それについてはすべてここで詳しく説明します 。 具体的な例を見てみましょう。



データ検索



Forsquare APIへの簡単なリクエストを例にとると、エッフェル塔の近くにあるオブジェクトのリストが返されます。



リクエストURL:

api.foursquare.com/v2/venues/search?v=20120602&ll=48.858%2C2.2944&client_secret=ILG5POWGBRBZDXLNPAGECAZOBC0KFPQAQ5SYOP51KFYANZ1B&client_id=HDEHROGPMARZ2O1JTK55VHXE4TTNGE0NQR4DBCKHFZULURJV>







応答を得るために、AFNetworkingを使用しました。 APIラッパークラス:



 + (LDFourSquareAPIClient *)sharedClient { static LDFourSquareAPIClient *_sharedClient = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedClient = [[LDFourSquareAPIClient alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]]; }); return _sharedClient; } - (id)initWithBaseURL:(NSURL *)url { if (self = [super initWithBaseURL:url]) { [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; [self setDefaultHeader:@"Accept" value:@"application/json"]; } return self; }
      
      







リクエストパラメータを作成します



  NSString *latLon = @"48.858,2.2944"; NSString *clientID = [NSString stringWithUTF8String:kCLIENTID]; NSString *clientSecret = [NSString stringWithUTF8String:kCLIENTSECRET]; NSDictionary *queryParams; queryParams = [NSDictionary dictionaryWithObjectsAndKeys:latLon, @"ll", clientID, @"client_id", clientSecret, @"client_secret", @"20120602", @"v", nil];
      
      







kCLIENTIDkCLIENTSECRETは認証のキーです。 あなたのアプリケーションを登録することができます、あなたは私のものを使うことができます。



サーバーへのリクエストとデータの受信は次のようになります。



  [[LDFourSquareAPIClient sharedClient] getPath:@"v2/venues/search" parameters:queryParams success:^(AFHTTPRequestOperation *operation, id responseObject) { NSArray *venues = [[responseObject objectForKey:@"response"] objectForKey:@"venues"]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { }];
      
      







場所配列には、データモデルにマップする場所のリストが含まれます。 JSON場所オブジェクト:



 categories = ( { icon = { name = ".png"; prefix = "https://foursquare.com/img/categories/building/default_"; sizes = ( 32, 44, 64, 88, 256 ); }; id = 4bf58dd8d48988d12d941735; name = "Monument / Landmark"; pluralName = "Monuments / Landmarks"; primary = 1; shortName = Landmark; } ); contact = { formattedPhone = "+33 892 70 12 39"; phone = "+33892701239"; }; hereNow = { count = 3; groups = ( { count = 3; items = ( ); name = "Other people here"; type = others; } ); }; id = 4adcda09f964a520dd3321e3; likes = { count = 0; groups = ( ); }; location = { address = "Parc du Champ de Mars"; city = Paris; country = France; crossStreet = "5 av. Anatole France"; distance = 42; lat = "48.85836229464931"; lng = "2.2945761680603027"; postalCode = 75007; state = "\U00cele de France"; }; name = "Tour Eiffel"; specials = { count = 0; items = ( ); }; stats = { checkinsCount = 31211; tipCount = 430; usersCount = 22307; }; url = "http://www.tour-eiffel.fr"; verified = 0; }
      
      







データモデル



この例のデータモデルは、2つのオブジェクト(場所と場所、およびそれらの間の1対1の関係)で構成されています。











データのインポート



サービスのJSON応答が「完璧」であり、そのモデルがデータベースのモデルと完全に一貫している場合、インポートするために、完了ブロックに次のコードを追加する必要があります。



 self.data = [Venue MR_importFromArray:venues];
      
      





データ配列全体をインポートする、または



 Venue *myVenue = [Venue MR_importFromObject:[venues objectAtIndex:0]];
      
      





単一のオブジェクトを作成します。



すぐに何らかの理由でMR_importFromArrayが機能しないことを予約します (githubのチケット )。そのため、インポートには次のコードを使用しました。



 NSMutableArray *arr = [NSMutableArray array]; for (NSDictionary *venueDict in venues) { [arr addObject:[Venue MR_importFromObject:venueDict]]; } self.data = arr;
      
      







残念ながら、サーバーからの応答は必ずしも正確であるとは限らず、オブジェクトの名前と関係がモデルと一致しないことがよくあります。 ここで、UserInfoディクショナリが助けになります。すべてのエンティティ、属性、またはリレーションシップがあります。 これらの各オブジェクトのマッピングを構成できます。



属性マッピングを再構成するには、この辞書に「mappedKeyName」-「JSONからの属性名」のペアを追加する必要があります。







また、このマッピングはKVCをサポートします。これは、モデルにネストされたエンティティを作成する必要がない場合に非常に便利です(Statsエンティティを削除し、チェックの数にアクセスできます)。







モデルの各オブジェクトには、primaryKeyのようなものが必要です。MagicalImportはobjectNameIDという名前の属性を検索するか、UserInfoでそのような属性を指定できます(例としてLocationとVenueの関係を使用)。







latを「主キー」として使用したことをおizeびします。当然、これは例のためだけに行われました。



コールバックをインポートする



インポートメカニズムは、処理されたデータのチェック/編集に使用できるコールバックを提供します(NSManagedObjectサブクラス内に実装):





たとえば、アドレスを含むLocationオブジェクトとのみ関係を確立することに基づいてチェックを実装します。

 - (BOOL)shouldImportLocation:(id)location { NSString *address = [location objectForKey:@"address"]; return address ? YES : NO; }
      
      







おわりに



考慮された例は些細なものであるため、MagicalImportのすべての複雑さ(およびドキュメントがまだないという事実)に精通することはできませんが、彼は考えた利点を感じることができるように思われます:余分なコードの欠如と実装の柔軟性データをインポートするとき。

テストプロジェクトはこちらにあります 。 (CocoaPodsはMagicalRecordとAFNetworkingの接続に使用されました )。



All Articles