핵심 데이터를 미리 채울 수 있는 방법이 있나요?
-
19-09-2019 - |
문제
저는 목록 앱을 만들고 이를 핵심 데이터로 뒷받침해 왔습니다.
사용자가 처음부터 시작할 필요가 없도록 공항 항목 10개의 기본 목록을 갖고 싶습니다.
이를 수행할 수 있는 방법이 있습니까?
도움을 주시면 감사하겠습니다.미리 감사드립니다.
해결책
가장 좋은 방법은 다음과 같습니다 (SQL 지식이 필요하지 않음).
목록 앱과 동일한 객체 모델을 사용하여 빠른 핵심 데이터 iPhone 앱 (또는 Mac 앱)을 만듭니다. 상점에 원하는 기본 관리 객체를 저장하기 위해 몇 줄의 코드를 작성하십시오. 그런 다음 시뮬레이터에서 해당 앱을 실행하십시오. 이제 ~/라이브러리/응용 프로그램 지원/iPhone 시뮬레이터/사용자/응용 프로그램으로 이동하십시오. Guids 중 응용 프로그램을 찾은 다음 SQLITE 저장소를 List App의 프로젝트 폴더에 복사하기 만하면됩니다.
그런 다음 Coredatabooks 예제에서와 같이 그 상점을로드하십시오.
다른 팁
예, 실제로 Coredatabooks 예제가이를 수행합니다. 여기에서 코드를 다운로드 할 수 있습니다. 샘플 코드
귀하가하는 일은 일반 절차를 사용하여 내부 상점 (데이터베이스)을 작성하여 다른 스토어와 마찬가지로 매장을 초기화하는 것입니다. 그러면 단순히 코드를 실행하고 Coredatabooks 예제 (아래 코드 스 니펫에 설명 된대로 코드를 실행하도록하십시오. ). 상점이 초기화되면 NSManagedObjectContext
생성 된 영구 저장소로 초기화하고 필요한 모든 엔티티를 삽입하고 컨텍스트를 저장하십시오.
컨텍스트가 성공적으로 저장되면 응용 프로그램을 중지 한 다음 Finder로 이동하여 폴더로 이동할 수 있습니다. ~/Library/Developer
검색을 입력하고 .sqlite를 입력하고 아래 /개발자를 봅니다. 날짜별로 정렬하면 코드가 실행되는 시간과 일치 해야하는 최신 .Qlite 데이터베이스가 제공됩니다. 그러면이 스토어를 가져 와서 프로젝트의 리소스로 추가 할 수 있습니다. . 그런 다음이 파일은 지속적인 매장 코디네이터가 읽을 수 있습니다.
- (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;
}
도움이되기를 바랍니다.
-oscar
이 방법을 사용하면 별도의 앱을 만들거나 SQL 지식이 필요하지 않습니다. 초기 데이터에 대한 JSON 파일 만 만들 수 있어야합니다.
객체로 구문 분석 한 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-tropreloadimport-existing-data-updated
10 개 항목의 경우 내 에서이 작업을 수행 할 수 있습니다. applicationDidFinishLaunching:
앱 대의원에서.
방법을 정의하십시오 insertPredefinedObjects
, 그것은 공항 품목 관리를 담당하는 엔티티의 사례를 생성하고 채우고 컨텍스트를 저장합니다. 파일에서 속성을 읽거나 단순히 코드에서 하드 와이어를 사용하여 단순히 하드 와이어 할 수 있습니다. 그런 다음이 메소드를 내부로 호출하십시오 applicationDidFinishLaunching:
.
Coredatabooks 예제 코드를 따를 때 iOS 데이터 저장 지침을 중단 할 수 있습니다.
https://developer.apple.com/icloud/documentation/data-storage/
(읽기 전용) 사전 인구 데이터베이스를 문서 디렉토리에 복사하는 데 앱이 거부되었으며 Apple은 사용자가 생성 한 파일에만 발생하기를 원합니다.
위의 지침은 몇 가지 솔루션을 제공하지만 대부분은 다음과 같습니다.
DB를 캐시스 디렉토리에 보관하고 OS가 캐시를 제거하는 상황을 우아하게 처리합니다. DB를 재건해야합니다.
DB 파일에서 'Do n't Cache Attribute'를 설정합니다. 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를 사용하는 것이 가장 좋겠다고 생각했습니다.그래서 저는 핵심 데이터를 배우고 데이터베이스를 미리 채우는 코드를 다시 작성하는 데 꽤 오랜 시간을 보냈습니다.두 플랫폼 모두에서 모든 단계를 수행하는 방법을 배우려면 많은 연구와 시행착오가 필요했습니다.제가 기대했던 것보다 겹치는 부분이 훨씬 적었습니다.
결국 나는 내 Android 프로젝트에서 동일한 SQLite 데이터베이스를 사용하기로 결정했습니다.그런 다음 FMDB 래퍼를 사용하여 iOS의 데이터베이스에 직접 액세스했습니다.혜택:
- 미리 채워진 데이터베이스를 한 번만 만들면 됩니다.
- 패러다임 전환이 필요하지 않습니다.Android와 FMDB 간의 구문은 다르지만 여전히 상당히 유사합니다.
- 쿼리 수행 방법을 훨씬 더 효과적으로 제어할 수 있습니다.
- 전체 텍스트 검색을 허용합니다.
Core Data를 배운 것을 후회하지는 않지만, 다시 공부한다면 SQLite만 고수했다면 시간을 많이 절약할 수 있었을 것입니다.
iOS에서 시작한 다음 Android로 이동할 계획이라면 FMDB와 같은 SQLite 래퍼나 다른 소프트웨어를 사용하여 데이터베이스를 미리 채울 것입니다.핵심 데이터로 미리 채워진 SQLite 데이터베이스를 기술적으로 추출할 수 있지만 스키마(테이블 및 열 이름 등)의 이름은 이상하게 지정됩니다.
그런데 미리 채워진 데이터베이스를 수정할 필요가 없다면 앱을 설치한 후 이를 문서 디렉터리에 복사하지 마세요.번들에서 직접 액세스하면 됩니다.
// 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)
이렇게 하면 두 개의 복사본이 없게 됩니다.
업데이트
나는 지금 사용한다 SQLite.swift FMDB보다는 Swift 프로젝트와 더 잘 통합되기 때문입니다.
기본값 저장을위한 또 다른 방법은 nsuserDefaults를 통해 찾을 수 있습니다. (놀라움!) 그리고 쉬운.
어떤 사람들이 제안한 것은 그것을 applicationDidFinishLaunching
주어진 10 개의 기본값의 경우, 공항 0 Thru 9
환경
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"];
...
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"];
[nud synchronize];
또는
[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]];
...
[[NSUserDefaults standardUserDefaults] synchronize];
그런 다음 기본값을받습니다.
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];
이것은 나를 위해 효과가있었습니다. 이것은 이것의 수정입니다 대답 ~에 의해 안드레아 토소 그리고 이것에 영감을 받았습니다 블로그. 답의 유일한 문제는 FileManager와 함께 SQLITE 파일을 이동할 때 데이터 손실 가능성이 있다는 것입니다. 필자 대신 대체물을 사용하여 약 500 행의 데이터를 저장했습니다.
1 단계
다른 앱에서 핵심 데이터를 채우고이 코드를 사용하여 파일의 경로를 가져옵니다.
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
2 단계
.sqlite 확장으로 3 개의 파일을 Xcode 프로젝트로 드래그하십시오. (대상에 추가 옵션을 선택하십시오).
STEP3
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
}
단계 4
이 기능을 AppDelegate.swift에 복사하여 SQLITE 데이터베이스를 이동 해야하는 URL을 가져옵니다.
func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
5 단계
PersistentContainer의 선언을 다음과 같이 대체하십시오.
// 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
}()