iOS 네트워크에서 Json이 CoreData로 자동 저장 요청(둘)
지난 일지에 이어 Json을 CoreData에 저장하는 것이 주요 내용이다.
사실대로 말하면 KVC가 값을 매기고 사고방식이 명확할 뿐이다. 그런데 나는 한 가지 문제를 생각하고 있다. 통용할 수 있는 방법이 없을까?그럼 문제야~ 굴착기 기술이 어느 쪽이 강해!
됐어, 됐어. KVC가 당분간 내 프로젝트의 수요를 만족시킬 수 있지만 그 일반적인 방법은 아직 찾고 있어. 능력에 한계가 있지만 노력할 거야.
겸사겸사 이 네트워크 요청 데이터를 CoreData에 저장하는 것에 대한 소개를 공유합니다
Process remote service data into Core Data
======================================================
Well, you have our data persisting to disk in a Property List format. But what you really want to do is to process it into Core Data. This is where you will be doing some heavy lifting and getting into the nitty gritty details. This is the part where the real magic happens!
To start off you will first need a way to retrieve the files from disk. Add -JSONDictionaryForClassWithName: in SDSyncEngine.m:
- (NSDictionary *)JSONDictionaryForClassWithName:(NSString *)className {
NSURL *fileURL = [NSURL URLWithString:className relativeToURL:[self JSONDataRecordsDirectory]];
return [NSDictionary dictionaryWithContentsOfURL:fileURL];
}
One caveat to the NSDictionary that -JSONDictionaryForClassWithName: returns is that the information you are interested in is will be in an NSArray with the key “results”. So to make things easier for processing purposes, add another method to access the data in the NSArray and spice it up a little to allow for sorting of the records by a specified key.
Add this beneath -JSONDictionaryForClassWithName: in SDSyncEngine.m:
- (NSArray *)JSONDataRecordsForClass:(NSString *)className sortedByKey:(NSString *)key {
NSDictionary *JSONDictionary = [self JSONDictionaryForClassWithName:className];
NSArray *records = [JSONDictionary objectForKey:@"results"];
return [records sortedArrayUsingDescriptors:[NSArray arrayWithObject:
[NSSortDescriptor sortDescriptorWithKey:key ascending:YES]]];
}
This method calls the previous method you implemented, and returns an NSArray of all the records in the response, sorted by the specified key.
You won’t really need the JSON responses that were saved to disk much past this point, so add another method to delete them when you’re finished with them. Add the following method above -JSONDictionaryForClassWithName:
- (void)deleteJSONDataRecordsForClassWithName:(NSString *)className {
NSURL *url = [NSURL URLWithString:className relativeToURL:[self JSONDataRecordsDirectory]];
NSError *error = nil;
BOOL deleted = [[NSFileManager defaultManager] removeItemAtURL:url error:&error];
if (!deleted) {
NSLog(@"Unable to delete JSON Records at %@, reason: %@", url, error);
}
}
In order to translate records from JSON to NSManagedObjects, you will need a few methods. First, you will need to translate the JSON values to Objective-C properties; the method you use will vary based on the remote service you are working with. In this case, you are using Parse which has a few “special” data types. The data you’ll be concerned with here are Files and Dates. Files are returned as URLs to the file’s location, and Dates are returned in the following format:
{
"__type": "Date",
"iso": "2011-08-21T18:02:52.249Z"
}
Since the date is in the format of a string, you will want some methods to convert from a Parse formatted date string to an NSDate and back to an NSString. NSDateFormatter can help with this, but they are very expensive to allocate — so first add a new NSDateFormatter property that you can re-use.
Add the dateFormatter property in your private category:
@interface SDSyncEngine ()
@property (nonatomic, strong) NSMutableArray *registeredClassesToSync;
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@end
And add these three methods above the #pragma mark -File Management.
- (void)initializeDateFormatter {
if (!self.dateFormatter) {
self.dateFormatter = [[NSDateFormatter alloc] init];
[self.dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"];
[self.dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
}
}
- (NSDate *)dateUsingStringFromAPI:(NSString *)dateString {
[self initializeDateFormatter];
// NSDateFormatter does not like ISO 8601 so strip the milliseconds and timezone
dateString = [dateString substringWithRange:NSMakeRange(0, [dateString length]-5)];
return [self.dateFormatter dateFromString:dateString];
}
- (NSString *)dateStringForAPIUsingDate:(NSDate *)date {
[self initializeDateFormatter];
NSString *dateString = [self.dateFormatter stringFromDate:date];
// remove Z
dateString = [dateString substringWithRange:NSMakeRange(0, [dateString length]-1)];
// add milliseconds and put Z back on
dateString = [dateString stringByAppendingFormat:@".000Z"];
return dateString;
}
The first method -initializeDateFormatter will initialize your dateFormatter property. The second method -dateUsingStringFromAPI: receives an NSString and returns an NSDate object. The third method -dateStringForAPIUsingDate: receives an NSDate and returns an NSString.
Take a little closer look, there, detective — the second and third methods do something a little strange. Parse uses timestamps in the ISO 8601 format which do not translate to NSDate objects very well, so you need to do some stripping and appending of the milliseconds and Z flag (used to denote the timezone). (Oh standards…there are so many wonderful ones to choose from!) :]
Next add this method below mostRecentUpdatedAtDateForEntityWithName:
- (void)setValue:(id)value forKey:(NSString *)key forManagedObject:(NSManagedObject *)managedObject {
if ([key isEqualToString:@"createdAt"] || [key isEqualToString:@"updatedAt"]) {
NSDate *date = [self dateUsingStringFromAPI:value];
[managedObject setValue:date forKey:key];
} else if ([value isKindOfClass:[NSDictionary class]]) {
if ([value objectForKey:@"__type"]) {
NSString *dataType = [value objectForKey:@"__type"];
if ([dataType isEqualToString:@"Date"]) {
NSString *dateString = [value objectForKey:@"iso"];
NSDate *date = [self dateUsingStringFromAPI:dateString];
[managedObject setValue:date forKey:key];
} else if ([dataType isEqualToString:@"File"]) {
NSString *urlString = [value objectForKey:@"url"];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *dataResponse = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
[managedObject setValue:dataResponse forKey:key];
} else {
NSLog(@"Unknown Data Type Received");
[managedObject setValue:nil forKey:key];
}
}
} else {
[managedObject setValue:value forKey:key];
}
}
This method accepts a value, key, and managedObject. If the key is equal to createdDate or updatedAt, you will be converting them to NSDates. If the key is an NSDictionary you will check the __type key to determine the data type Parse returned. If it is a Date, you will convert the value from an NSString to an NSDate. If it is a File, you will do a little more work since you are interested in getting the image itself!
To get the image, send off a request to download the image file. It is important to note that downloading the image data can take a considerable amount of time, so this may only work efficiently with smaller data sets. Another solution would be to fetch the image data when the record is accessed (lazy loading), but it would only be available if the user has an Internet connection at the time of lazy loading.
If the data type is anything other than a File or Date there is no way to know what to do with it so set the value to nil. In any other case you will simply pass the value and key through untouched and set them on the managedObject.
뒷말:
인내심을 가지고 여기를 보면, 사실 나는 조금이라도 증명하고 싶은데, 역시 KVC다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.