지난 번 포스트에 이어서 이번에는 PhotoKit에 변경 요청을 하는 법에 대해서 알아보도록 하겠습니다.

이 포스트는 다음 문서를 참고하여 작성되었습니다. Requesting Changes to the Photo Library

PHAsset, PHAssetCollection, PHCollectionList 객체는 모두 불변(immutable) 객체입니다. 따라서 이 객체들의 값을 변경해서 다시 돌려보내는 방법으로는 변경 요청을 할 수 없습니다. PhotoKit은 다음과 같이 변경 요청을 보내도록 하고 있습니다.

  1. PHPhotoLibrary 타입의 싱글턴 변수를 받아옵니다. PHPhotoLibrary 타입은 Photos 앱에 의해 관리되는 모든 사진과 비디오의 집합을 나타냅니다.

     let library = PHPhotoLibrary.shared() 
    
  2. performChange 계열의 함수를 호출해야 합니다. 인자로 줄 클로저 내용은 다음 단계에 작성할 예정입니다.

     func performChanges(() -> Void, completionHandler: ((Bool, Error?) -> Void)?) // 변경 요청을 비동기적으로 한다.
     func performChangesAndWait(() -> Void) // 변경 요청을 동기적으로 한다.
    
  3. 변경하고자 하는 대상에 맞는 ChangeRequest를 선정합니다.

    class PHAssetChangeRequest: PHChangeRequest // PHAsset에 대한 변경 요청
    class PHAssetCollectionChangeRequest: PHChangeRequest // PHAssetCollection에 대한 변경 요청  
    class PHCollectionListChangeRequest: PHChangeRequest // PHCollectionList에 대한 변경 요청  
    
  4. 3번에서 고른 ChangeRequest 타입은 각각의 타입에 맞는 생성,삭제 클래스 메소드, 업데이트 프로퍼티를 제공합니다.(CRUD중 C,U,D가 가능합니다. R은 지난 번 포스트에서 다루고 있습니다) 원하는 동작과 현재 가지고 있는 데이터에 맞는 동작을 선택합니다.

    자세한 메소드와 프로퍼티 목록은 공식 문서를 참고해주세요.
    PHAssetChangeRequest
    PHAssetCollectionChangeRequest
    PHCollectionListChangeRequest

  5. 첫번째 클로저를 작성합니다. 생성, 삭제, 변경의 경우 모두 코드가 조금씩 다르므로 몇가지 예시로 보이도록 하겠습니다.

     //UIImage를 받아 저장하는 함수 
     func save(image: UIImage) {
         PHPhotoLibrary.shared().performChanges({
             let request = PHAssetChangeRequest.creationRequestForAsset(from: image)
    
             //일부 프로퍼티를 설정할 수 있다(생성 일자, 위치, 좋아요 여부, 숨김 여부)
         }, completionHandler: nil)
     }
    
     //사진을 삭제하는 함수
     func delete(asset: PHAsset) {
         PHPhotoLibrary.shared().performChanges({
             PHAssetChangeRequest.deleteAssets([asset] as NSArray) // 배열에 담아서 NSArray로 바꿔줘야 합니다. 정확히는 NSFastEnumerator를 상속받은 클래스면 됩니다.
         }, completionHandler: nil)
     }
    
     //좋아요를 껏다 키는 함수
     func toggleFavoriteForAsset(asset: PHAsset) {
     PHPhotoLibrary.shared().performChanges({
                 let request = PHAssetChangeRequest(for: asset)
    
                 request.isFavorite = !asset.isFavorite
    
             }, completionHandler: nil)
     }
    

    첫번째 생성 예시에서 UIImage를 이용해서 사진첩에 사진을 생성합니다. 하지만 UIImage는 메타데이터를 가지고 있지 않기 때문에 이런 방식으로 생성하게 되면 메타데이터가 모두 날아갑니다. 이 메타데이터를 보존하기 위해서는, Image I/O를 이용해 임시 파일을 만든 뒤, 파일 URL을 이용한 생성 함수를 이용해 생성 요청을 보내야 합니다.

  6. 필요시 completionHandler를 작성하여 제공합니다. 클로저의 첫 인자은 성공,실패 여부를 나타내는 Bool 값이며, 두번째 인자는 실패시에만 받을 수 있는 error값입니다.


이상으로 PhotoKit에 사용법에 대해서 알아보았습니다. 앞선 포스트까지 해서 옵저버 등록, 권한 획득 등을 제외한 모든 기능을 돌아보았습니다. 처음에는 PhotoKit이 참 어렵다고 생각했는데, 막상 파고보니 아주 간단하게 끝났습니다. 다음에 더 좋은 내용으로 찾아오겠습니다:)