핵심 데이터를 미리 채우는 방법이 있습니까?
저는 목록 앱을 만들고 핵심 데이터로 지원하고 있습니다.
사용자가 처음부터 시작할 필요가 없도록 10 개의 공항 항목에 대한 기본 목록을 갖고 싶습니다.
이것을 할 방법이 있습니까?
도움을 주시면 감사하겠습니다. 미리 감사드립니다.
다음은 가장 좋은 방법입니다 (SQL 지식이 필요하지 않음).
List 앱과 동일한 개체 모델을 사용하여 빠른 Core Data iPhone 앱 (또는 Mac 앱)을 만듭니다. 몇 줄의 코드를 작성하여 저장하려는 기본 관리 개체를 저장합니다. 그런 다음 시뮬레이터에서 해당 앱을 실행하십시오. 이제 ~ / Library / Application Support / iPhone Simulator / User / Applications로 이동합니다. GUID에서 애플리케이션을 찾은 다음 sqlite 스토어를 List 앱의 프로젝트 폴더에 복사하면됩니다.
그런 다음 CoreDataBooks 예제 에서처럼 해당 저장소를로드합니다.
예, 실제로 CoreDataBooks 예제가이 작업을 수행합니다. 여기에서 코드를 다운로드 할 수 있습니다. 샘플 코드
일반적인 절차를 사용하여 내부 저장소 (데이터베이스)를 생성하여 다른 저장소와 마찬가지로 저장소를 초기화 한 다음 코드를 실행하고 CoreDataBooks 예제 (아래 코드 조각)에 설명 된대로 코드를 실행하게합니다. ). 저장소가 초기화되면 NSManagedObjectContext생성 된 영구 저장소를 만들어 초기화하고 필요한 모든 엔터티를 삽입 한 다음 컨텍스트를 저장해야합니다.
컨텍스트가 성공적으로 저장되면 응용 프로그램을 중지 한 다음 파인더로 이동하여 폴더로 이동합니다. ~/Library/Developer검색 .sqlite를 입력하고 / Developer 아래를 살펴 봅니다. 날짜별로 정렬하면 일치해야하는 최신 .sqlite 데이터베이스가 제공됩니다. 코드가 실행 된 시간에이 저장소를 가져와 프로젝트의 리소스로 추가 할 수 있습니다. 그런 다음 영구 저장소 조정자가이 파일을 읽을 수 있습니다.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"CoreDataBooks.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataBooks" ofType:@"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator;
}
도움이되기를 바랍니다.
-오스카
이 방법을 사용하면 별도의 앱을 만들거나 SQL 지식이 필요하지 않습니다. 초기 데이터에 대한 JSON 파일 만 만들 수 있으면됩니다.
객체로 구문 분석 한 다음 Core Data에 삽입하는 JSON 파일을 사용합니다. 앱이 초기화 될 때 이렇게합니다. 또한이 초기 데이터가 이미 삽입되었는지 여부를 나타내는 하나의 항목을 핵심 데이터에 만듭니다. 초기 데이터를 삽입 한 후이 항목을 설정하여 다음에 스크립트를 실행할 때 초기 데이터가 이미 초기화되었음을 확인합니다.
json 파일을 객체로 읽으려면 :
NSString *initialDataFile = [[NSBundle mainBundle] pathForResource:@"InitialData" ofType:@"json"];
NSError *readJsonError = nil;
NSArray *initialData = [NSJSONSerialization
JSONObjectWithData:[NSData dataWithContentsOfFile:initialDataFile]
options:kNilOptions
error:&readJsonError];
if(!initialData) {
NSLog(@"Could not read JSON file: %@", readJsonError);
abort();
}
그런 다음 다음과 같이 엔티티 객체를 만들 수 있습니다.
[initialData enumerateObjectsUsingBlock:^(id objData, NSUInteger idx, BOOL *stop) {
MyEntityObject *obj = [NSEntityDescription
insertNewObjectForEntityForName:@"MyEntity"
inManagedObjectContext:dataController.managedObjectContext];
obj.name = [objData objectForKey:@"name"];
obj.description = [objData objectForKey:@"description"];
// then insert 'obj' into Core Data
}];
이 작업을 수행하는 방법에 대한 자세한 설명을 보려면이 자습서를 확인하십시오. http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated
10 개 항목의 경우 applicationDidFinishLaunching:앱 델리게이트 내에서이 작업을 수행 할 수 있습니다 .
insertPredefinedObjects공항 항목 관리를 담당하는 엔터티의 인스턴스를 만들고 채우는 메서드를 정의하고 컨텍스트를 저장합니다. 파일에서 속성을 읽거나 코드에서 간단히 하드 와이어 할 수 있습니다. 그런 다음이 메서드를 applicationDidFinishLaunching:.
CoreDataBooks 예제 코드를 따를 때 iOS 데이터 저장 지침을 위반할 가능성이 있음을 명심하십시오.
https://developer.apple.com/icloud/documentation/data-storage/
(읽기 전용) 미리 채워진 데이터베이스를 문서 디렉토리에 복사하는 앱이 거부되었습니다 (그런 다음 iCloud에 백업 됨). 애플은 사용자 생성 파일에만 해당 작업이 발생하기를 원합니다.
위의 지침은 몇 가지 해결책을 제공하지만 대부분 다음과 같이 요약됩니다.
캐시 디렉토리에 DB를 저장하고 OS가 캐시를 제거하는 상황을 적절하게 처리합니다. DB를 재 구축해야하므로 대부분의 경우이를 배제 할 수 있습니다.
DB 파일에 '캐시하지 않는 속성'을 설정합니다. 이는 OS 버전에 따라 다르게 수행해야하므로 약간 난해합니다.
너무 까다 롭다고 생각하지 않지만 예제 코드가 iCloud와 함께 작동하도록하려면 약간의 추가 작업이 필요합니다.
그래서 저는 사전 (아마도 JSON에서)에서로드하고 데이터베이스를 채우는 일반 메서드를 개발했습니다. 신뢰할 수있는 데이터 (안전한 채널에서)에만 사용해야하며 순환 참조를 처리 할 수 없으며 스키마 마이그레이션이 문제가 될 수 있습니다.하지만 저와 같은 간단한 사용 사례의 경우에는 괜찮습니다.
여기 간다
- (void)populateDBWithDict:(NSDictionary*)dict
withContext:(NSManagedObjectContext*)context
{
for (NSString* entitieName in dict) {
for (NSDictionary* objDict in dict[entitieName]) {
NSManagedObject* obj = [NSEntityDescription insertNewObjectForEntityForName:entitieName inManagedObjectContext:context];
for (NSString* fieldName in objDict) {
NSString* attName, *relatedClass, *relatedClassKey;
if ([fieldName rangeOfString:@">"].location == NSNotFound) {
//Normal attribute
attName = fieldName; relatedClass=nil; relatedClassKey=nil;
} else {
NSArray* strComponents = [fieldName componentsSeparatedByString:@">"];
attName = (NSString*)strComponents[0];
relatedClass = (NSString*)strComponents[1];
relatedClassKey = (NSString*)strComponents[2];
}
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", attName ]);
NSMethodSignature* signature = [obj methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:selector];
//Lets set the argument
if (relatedClass) {
//It is a relationship
//Fetch the object
NSFetchRequest* query = [NSFetchRequest fetchRequestWithEntityName:relatedClass];
query.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:relatedClassKey ascending:YES]];
query.predicate = [NSPredicate predicateWithFormat:@"%K = %@", relatedClassKey, objDict[fieldName]];
NSError* error = nil;
NSArray* matches = [context executeFetchRequest:query error:&error];
if ([matches count] == 1) {
NSManagedObject* relatedObject = [matches lastObject];
[invocation setArgument:&relatedObject atIndex:2];
} else {
NSLog(@"Error! %@ = %@ (count: %d)", relatedClassKey,objDict[fieldName],[matches count]);
}
} else if ([objDict[fieldName] isKindOfClass:[NSString class]]) {
//It is NSString
NSString* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
} else if ([objDict[fieldName] isKindOfClass:[NSNumber class]]) {
//It is NSNumber, get the type
NSNumber* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
}
[invocation invoke];
}
NSError *error;
if (![context save:&error]) {
NSLog(@"%@",[error description]);
}
}
}
}
그리고 json에서로드 ...
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"initialDB" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSError* error;
NSDictionary *initialDBDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
[ self populateDBWithDict:initialDBDict withContext: [self managedObjectContext]];
JSON 예
{
"EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
"EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
}
과
{
"Country": [{"Code": 55, "Name": "Brasil","Acronym": "BR"}],
"Region": [{"Country>Country>code": 55, "Code": 11, "Name": "Sao Paulo"},
{"Country>Country>code": 55, "Code": 31, "Name": "Belo Horizonte"}]
}
객체가 존재하는지 확인하고 그렇지 않은 경우 일부 데이터로 생성하는 것은 어떻습니까?
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Settings"];
_managedObjectSettings = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
if ([_managedObjectSettings count] == 0) {
// first time, create some defaults
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Settings" inManagedObjectContext:managedObjectContext];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"speed"];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"sound"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey:@"aspect"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: @"useH264"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: @"useThumbnail"];
NSError *error = nil;
// Save the object to persistent store
if (![managedObjectContext save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
}
이 답변은
- 앱에 미리 채워진 데이터베이스 포함
- 여러 플랫폼 (iOS, Android 등) 용 앱 만들기
Android 앱용으로 미리 채워진 SQLite 데이터베이스를 만들었습니다. 그런 다음 iOS 버전의 앱을 만들 때 Core Data를 사용하는 것이 가장 좋을 것이라고 생각했습니다. 그래서 저는 Core Data를 배우고 데이터베이스를 미리 채우기 위해 코드를 다시 작성하는 데 꽤 오랜 시간을 보냈습니다. 두 플랫폼 모두에서 모든 단계를 수행하는 방법을 배우려면 많은 연구와 시행 착오가 필요했습니다. 내가 기대했던 것보다 훨씬 적은 겹침이 있었다.
결국 저는 Android 프로젝트에서 동일한 SQLite 데이터베이스를 사용하기로 결정했습니다. 그런 다음 FMDB 래퍼를 사용하여 iOS의 데이터베이스에 직접 액세스했습니다. 혜택:
- 미리 채워진 데이터베이스를 한 번만 만들면됩니다.
- Doesn't require a paradigm shift. The syntax between Android and FMDB, while different, is still fairly similar.
- Have a lot more control over how Queries are performed.
- Allows full text search.
Although I don't regret learning Core Data, if I were to do it over I could have saved a lot of time by just sticking to SQLite.
If you are starting in iOS and then planning to move to Android, I would still use a SQLite wrapper like FMDB or some other software to prepopulate the database. Although you can technically extract the SQLite database that you prepopulate with Core Data, the schema (table and column names, etc.) will be strangely named.
By the way, if you don't need to modify your prepopulated database, then don't copy it to the documents directory after the app is installed. Just access it directly from the bundle.
// get url reference to databaseName.sqlite in the bundle
let databaseURL: NSURL = NSBundle.mainBundle().URLForResource("databaseName", withExtension: "sqlite")!
// convert the url to a path so that FMDB can use it
let database = FMDatabase(path: databaseURL.path)
This makes it so that you don't have two copies.
Update
I now use SQLite.swift rather than FMDB, because it integrates better with Swift projects.
Another method for storing defaults is found by way of NSUserDefaults. (surprise!) And its easy.
Suggested by some, put that into the applicationDidFinishLaunching
In the given case of 10 defaults, Airport0 thru 9
Setting
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"];
...
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"];
[nud synchronize];
or
[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]];
...
[[NSUserDefaults standardUserDefaults] synchronize];
And then, getting the defaults.
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];
This worked for me. This is a modification of this answer by Andrea Toso and inspired by this blog. The only issue with the answer is that there is a chance of data loss when moving sqlite files with FileManager. I saved around 500 rows of data by using replacePersistentStore instead of FileManager.default.copyItem
Step 1
Populate your Core Data in another app and get files' path using this code:
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
Step2
Drag your 3 files with .sqlite extension into your xCode project. (Be sure to select Add to targets option).
Step3
Create function to check app's first run in AppDelegate.swift
func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
Step4
Copy this function in AppDelegate.swift to get url where sqlite database should be moved:
func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
Step 5
Replace declaration of persistentContainer with this one:
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ProjectName")
let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")
if UserDefaults.isFirstLaunch() {
let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
참고URL : https://stackoverflow.com/questions/2230354/any-way-to-pre-populate-core-data
'Nice programing' 카테고리의 다른 글
| 자바 스크립트 세트 대 어레이 성능 (0) | 2020.11.27 |
|---|---|
| 새로운 Django 메시지 프레임 워크의 메시지에 HTML을 어떻게 출력합니까? (0) | 2020.11.27 |
| urllib2의 시간 초과 처리? (0) | 2020.11.27 |
| Rails : Partials는 인스턴스 변수를 인식해야합니까? (0) | 2020.11.26 |
| iPhone의 StatusBar에보기 추가 (0) | 2020.11.26 |