CloudKit Quick Start - Adding Reference Fields CloudKit


Adding Reference Fields

https://developer.apple.com/library/archive/documentation/DataManagement/Conceptual/CloudKitQuickStart/AddingReferences/AddingReferences.html

모델 객체 사이에 관계를 표현하려면 스키마에 리퍼런스 필드를 사용한다. 예를 들어, 계층 데이터나 소유권을 나타낼때이다. 이 챕터는 레코드에 어떻게 리퍼런스를 추가하는지를 설명하며 리퍼런스로 레코드를 저장하고 페치하는 것을 나타내며 소유권을 지정하여 연관 레코드가 자동적으로 사라지게 한다.

스키마에서 관계 모델링에 대해

리퍼런스 필드 형식을 사용해 모델 객체들 사이에서 일대일 또는 일대다 관계를 표시할 수 있다. 코드내에서 리퍼런스 필드는 CKReference 객체이며 대상 레코드에 대한 레코드 아이디를 캡슐화하고 소스레코드에 추가된다. 스키마에서 일대일 관계를 표현하려면 소스 레코드 형식에 리퍼런스 필드를 추가한다.

../Art/to_one_relationship_2x.png

모델 객체사이에서 일대다 관계를 표현하려면, 자식 레코드에서의 리퍼런스가 부모레코드를 가리킬 때 더 효율적이다. 즉, 자식 레코드에 리퍼런스 필드를 추가한다. 자식레코드는 소스이고 부모 레코드는 이 스키마에서의 대상이된다.

예를 들어, Artist와 Artwork 모델사이에는 일대다 관계가 있고 아트워크에서 아티스트 모델로는 일대일관계가 있다.
../Art/gallery_object_model_2x.png
스키마에서 이들 관계를 표현하려면 아트워크 레코드에 artist라는 리퍼런스 필드를 추가한다. 이 리퍼런스 필드는 Artist 레코드의 레코드 아이디를 포함하게 된다.

../Art/to_many_relationship_2x.png

비슷하게, 객체 모델에서 Artist에서 Collection으로 일대다를 표현하려면 Collection레코드에 리퍼런스 필드를 추가한다. 이들레코드를 페치한 후 모델 객체사이에 적절하게 일대일 그리고 일대다 관계를 생성한다.

리퍼런스 필드 생성

디벨롭먼트 중에, 스키마를 생성하기 위해 리퍼런스 필드를 포함하는 레코드를 저장한다. 소스 레코드에 CKReference 객체를 추가함으로서 스키마에 일대일관계를 표현한다.

한 레코드에서 다른 것으로의 레코드를 생성하려면
1. 대상 레코드를 위한 레코드 아이디를 생성하거나 얻는다
CKRecordID* artistRecordID = [[CKRecordID alloc] initWithRecordName:@"Mei Chen"];

2. 대상의 레코드 아이디를 파라미터로 건냄으로서 리퍼런스 객체를 생성한다
CKReference* artistReference = [[CKReference alloc] initWithRecordID:artistRecordID action:CKReferenceActionNone];

3. 소스 레코드에 리퍼런스 객체 추가하기
CKRecord* artworkRecord;
...
artworkRecord[@"artist"] = artistReference;

레코드 형식을 생성하기 위해 소스레코드를 저장한다., as described in Initializating the Container. . 만약  이들 사이의 리퍼런스를 포함하는 다중 레코드를 저장하기를 원하면 단일 연산에서 모든 레코드를 저장한다. as described in Batch Operations to Save and Fetch Multiple Records.. 클라우드킷은 소스레코드가 저장되기전에 대상 레코드가 저장되도록 해준다.

리퍼런스 필드로 레코드 페치하기
 
모델 뷰 컨트롤러 디자인 패턴에 있는 모델 객체로 레코드(CKRecord)를 사용하지 않아야 한다. 대신 페치된 레코드로부터 분리된 모델 객체를 생성하는데, 특히 페치된 레코드가 리퍼런스를 포함하면 더욱 그렇다. 앱은 레코드 사이의 리퍼런스를 해석하는 책임을 가지고 모델 객체사이에 적절한 관계를 생성한다는 책임을 갖기 때문이다. 소스레코드가 페치될 때 대상 레코드가 자동적으로 페치되는 것은 아니다. 앱은 대상 레코드가 필요할 때 페치해야하는 책임을 가진다. 리퍼런스가 표현하는 관계 형식에 기반해 대상과 소스 레코드를 페치할 다른 방법이 있다.

가능하면, 모델 객체 상이의 관계를 얻기위해 배치 페치를 한다. as described in Batch Operations to Save and Fetch Multiple Records.

일대일 관계 리졸브

일대일관계를 위해, 소스 레코드로부터 참조 필드를 얻고 연관된 대상 레코드를 페치한다.

일대일관계의 대상 페치하려면,
1. 참조 필드를 얻는다
CKRecord* artworkRecord;
...
CKReference* referenceToArtist = artworkRecord[@"artist"];

2. 참조에서 대상 레코드 아이디를 얻는다.
CKRecordID* artistRecordID = artistReference.recordID;

3. 대상 레코드를 페치한다.
[publicDatabase fetchRecordWithID:artistRecordID completionHandler:^(CKRecord* artistRecord, NSError* error) {
  if (error) {
    // failed to fetch record
  }
  else {
    // 레코드를 성공적으로 페치했다.
  }
}];

fetchRecordWithID:completionHandler: 에 코드를 추가한다. 예를들어, 연관된 Artwork와 Artist 객체사이에 일대일관계를 생성하는 코드를 추가한다.

일대다 관계 해결하기

일대다 관계에 대해서는 프리디케이트를 사용해 부모 레코드의 모든 자식을 전부 페치한다. 그 대상 레코드로 부모를 가지는 모든 레코드를 패치할 수 있다.

일대다 관계의 자식들을 페치하려면,
1. 이전에 페치한 그리고 부모의 모델객체인 부모 레코드 아이디 (CKRecordID)로부터 시작한다.
예를들어, Artist레코드로부터 Artist 모델을 생성한다.
__block Artist* artist = [[Artist alloc] initWithhRecord:artistRecord];
__block을 사용해 completion 핸들러에서도 부모객체에 접근할 수 있게 한다.

2. 자식 레코드를 얻기위해 프리디케이트 객체를 생성한다.
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"artist = %@", artistRecordID];
자신의 코드내에서는, 자식레코드내의 참조필드의 이름을 artist로 뒤바꾸고 artistRecordID로 부모 레코드 아이디를 뒤바꾼다.
일러두기: 프리디케이트 형식 문자열은 CKRecord, CKRecordID, CKReference 객체도 사용할 수 있다.

3. 검색하기 위해 지정한 레코드 형식으로 쿼리 객체를 생성한다.
CKQuery* query = [[CKQuery alloc] initWithRecordType:@"Artwork" predicate:predicate];
자신의 코드내에서는 자식 레코드 형식의 이름을 @"Artwork"로 뒤바꾼다.

4. 페치를 실행한다.
CKDatabase* publicDatabase = [[CKContainer defaultContainer] publicCloudDatabase];
[publicDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray* results, NSError* error) {
  if (error) {
    // 부모의 자식들을 페치하는데 실패했다.
  }
  else {
    // 각 자식의 모델 객체를 생성하고 부모로부터 그 자식들로의 일대다 관계를 설정한다.
  }
}];

else 문을 추가해 모델 객체 사이의 연관된 관계를 생성한다.

다중 레코드를 저장하고 페치하는 배치 연산들

단일 연산으로 다중 레코드를 저장하고 페치하는 것이 효율적이다. 특히 레코드 형식이 참조형식인 경우 그렇다. CKModifyRecordsOperation 객체를 사용해 단일 연산으로 새로운 소스나 대상 레코드를 저장할 수 있다. 지정자가 없는 대상 레코드로의 참조를 생성할 수 있다. 소스와 대상 레코드를 함께 저장하기만 한다면 클라우드 킷은 연관된 CKRecord 객체를 유지하며 대상 레코드는 소스레코드를 저장하기 전에 저장한다. CKFetchRecordsOperation 객체를 사용해 단일 연산으로 소스레코드 집합을 페치할 수 있다.

단일 연산으로 다중 레코드를 페치하려면
1. 배열에 페치할 레코드에 대한 모든 레코드 아이디를 추가한다.
  다중 일대일 관계를 페치하려면 참조 필드의 대상 레코드 아이디를 배열에 저장한다.

2. 레코드 아이디의 배열을 파라미터로 전달함으로서 페치 레코드 연산 객체를 생성한다.
CKFetchRecordsOperation* fetchRecordsOperation = [[CKFetchRecordsOperation alloc] initWithRecordIDs:fetchRecordIDs];

3. 선택적으로,  레코드별 완료 핸들러를 제공한다.
각 레코드로 부터 데이터를 저장하려면 레코드당 완료 핸들러를 제공한다. 완료 핸들러는 성공적으로 페치된 레코드를 위한 모델 객체를 생성하고 연산이 실패하면 실패한 페치에 대한 레코드 아이디를 저장할 수 있다.
fetchRecordsOperation.perRecordCompletionBlock = ^(CKRecord* record, CKRecordID* recordID, NSError* error) {
  if (error) {
    // 실패한 패치에 대한 레코드 아이디를 유지
  }
  else {
    // 모델 객체를 생성하고 다른 모델로의 관계를 설정한다
  }
};

4. 전체 연산에 대한 완료 핸들러를 설정한다.
fetchRecordsOperation.fetchRecordsCompletionBlock = ^(NSDictionary* recordsByRecordID, NSError* error) {
  if (error) {
    // 전체나 일부의 레코드를 얻는데 실패함
  }
  else {
    // 연관된 뷰를 업데이트
  }
};
만약 레코드당 완료 핸들러내의 레코드 아이디를 저장하려면 실패한 패치에 다시 시도할 수 있다.

5. 연산시작
fetchRecordsOperation.database = [[CKContainer defaultContainer] publicCloudDatabase];
[fetchRecordsOperation start];

연관 레코드 자동 삭제를 위한 소유권 지정하기

대상 레코드가 삭제되었을 때 제거되야 할 참조의 소스 레코드를 지정할 수 있다. 예를 들어, 일대다 관계의 자식들을 부모가 삭제될 때 삭제되기를 원한다. 부모 레코드는 자식 레코드를 소유한다. 만약 자식이 다른 레코드를 소유하면 이 것또한 삭제되며 삭제의 종속이 일어난다.

../Art/cascade_deletes_2x.png


참조 객체를 생성할 때 삭제 액션을 지정한다. 대상이 삭제될 때 소스레코드를 삭제하려면 대상 레코드를 건내며 CKReferenceActionDeleteSelf 를 액션 파라미터로서 initWithRecord:action: 메소드에 전달한다. 갤러리 샘플에서 아티스트에 소유된 아트워크를 지정하여 아티스트가 삭제될 때 삭제되도록 한다.

CKReference* referenceToArtist = [[CKReference alloc] initWithRecord:artistRecord action:CKReferenceActionDeleteSelf];
artworkRecord[@"artist"] = referenceToArtist;

대체하여, 각 레코드에 대한 클라우드킷 대쉬보드에서 DeleteSelf 박스를 체크한다.

클라우드킷은 CKReferenceActionDeleteSelf 참조의 첫 번째 대상 레코드가 삭제될 때 소스레코드를 삭제한다. 만약 소스레코드가 다중 CKReferenceActionDeleteSelf 참조를 가지면 다른 대상 레코드가 조재하는 몇몇에도 삭제될 수 있으며 모순된 데이터 모델이 된다. 스키마를 설계하여 각 레코드가 하나를 초과하는 CKReferenceActionDeleteSelf 참조를 갖지 않게 한다.

../Art/first_delete_wins_2x.png









덧글

댓글 입력 영역