프로젝트를 진행하던 중, Window와의 데이터 공유가 필요한 사항이 있었고 기존 파일과의 혼동을 피하기 위해 커스텀 파일을 구현하고 해당 파일에 데이터를 저장해야하는 작업을 진행하게 되었다. 처음에는 막연히 Document에 저장할 때, 확장자명만 바꿔주면 되지 않을까라는 생각을 했지만 이 경우에는 파일을 직접 열 때나 앱에서 해당 파일을 불러와야할 때 문제가 생길 수 밖에 없었다.
처음하는 작업이라 막막하였으나 다행히 앱의 자체 타입을 사용할 수 있게끔 해주는 방법을 찾을 수 있었고, 이와 함께 UTI라는 존재를 알게 되었다.
UTI란 무엇인가
UTI는 Uniform Type Identifiers의 축약어로 iOS와 macOS에서는 이를 통해서 공통 시스템 오브젝트의 데이터 유형을 식별해준다. 기본적으로 파일 확장자의 식별에서는 PNG 이미지를 식별할 때에는 아래와 같은 방식으로 구분하였다.
- 'PNG' 파일 형식 별로
- 파일 이름의 확장자가 ".png"인지
- mime 유형에서는 "png/mime"로
그러나 UTI는 리버스 DNS 표기를 따른 플레인 문자열로 대체된다.
- public.png
- com.blabla
"public."으로 시작되는 UTI는 Apple만 정의 가능하며, 커스텀일 경우에는 위와 같은 리버스 DNS 표기를 따라 정의해야 한다. 그 외로 UTI는 데이터 유형 분류 메커니즘도 제공하는데 이러한 세부적인 요소는 아래의 링크의 글에서 잘 정리되었으니 확인해보면 좋을 듯 하다.
Exported와 Imported
UTI는 Exported와 Imported 두 가지로 분류되는데 본인이 말한 커스텀 타입을 정의하기 위해서는 Exported를 통해서 정의해야 하며, 시스템이 해당 문서의 유형을 식별해줄 수 있게 해준다. Imported는 이와 반대로 이미 사용되는 타입을 정의할 때 사용되며, 기존의 타입이 존재하긴하나 어플리케이션에서 사용해야될 경우에는 정의를 해주는 것이 좋다고 한다.
Exported UTI를 통한 Custom Document 생성
먼저 info로 이동하여 exported Type Identifiers를 생성한다.
위의 이미지를 보면 몇 가지 내용을 정의해두었는데 각각의 요소는 아래와 같다.
- Description: 말 그대로 해당 Type이 어떤 용도인지와 같은 설명을 기입
- Identifier: 해당 UTI를 식별하는 식별자로 리버스 DNS 표기 필요
- ConformsTo: 해당 UTI가 어떤 타입의 서브 타입인지. public.image, public.data 같이 필요한 타입으로 정의
- Extensions: 실제 파일 생성 시의 확장자 명
이제는 소스 코드에서 해당 타입을 호출할 필요가 있을 수 있으므로 이를 위해 UTType에 extension으로 본인이 정의한 타입을 작성해주어야 한다. 다만 앞서 얘기하지 못 하였으나 UTType이라는 것 자체가 iOS 14 이후로 사용이 가능하다보니, @available 정의가 필요하다.
import UniformTypeIdentifiers
@available(iOS 14.0, *)
extension UTType {
static var mcft: UTType {
UTType(exportedAs: "com.myCustom.fileType")
}
}
UTType을 extension하여 정의하기 위해서는 UniformTypeIdentifiers를 import해줘야 하며, exportedAs에는 본인이 정의했던 식별자를 작성해주면 된다. 참고로 iOS 14 아래의 버전에서는 UTType을 쓰지 못하므로 String에 extension으로 식별자를 정의해준다.
extension String {
static var mcftType: String {
return "com.myCustom.fileType"
}
}
이렇게 정의하는 이유는 이후에 DocumentPickerVC를 생성할 때, 불러올 수 있는 확장자를 정의해줘야 하기 때문에 iOS 버전에 따라 각각의 식별자 등록을 진행해줘야 한다.
이제 마지막으로 Exported할 UTI를 Doucument Type으로 정의해주는 작업까지 해주면 된다.
Name 섹션은 본인이 원하는 대로 작성하고, Types 섹션의 경우에는 Exported에서 기입한 Identifier를 그대로 입력해주어야 한다. Handler Rank의 경우 본인이 직접 Exproted하는 Document이니 Owner로 설정해주면 된다.
이후 UIDocumentPickerViewController를 통해 본인이 커스텀한 파일을 사용해주면 되는데 iOS14 이전과 이후 버전에 따라 init의 매개변수가 다르니 적절하게 분기 처리를 하여 앞서 작성한 UTType이나 String을 활용해주면 된다.
if #available(iOS 14.0, *) {
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.mcft])
picker.delegate = self
picker.allowsMultipleSelection = false
picker.modalPresentationStyle = .fullScreen
present(picker, animated: true, completion: nil)
} else {
let picker = UIDocumentPickerViewController(documentTypes: [String.mcftType], in: .open)
picker.delegate = self
picker.allowsMultipleSelection = false
picker.modalPresentationStyle = .fullScreen
present(picker, animated: true, completion: nil)
}
프로젝트를 진행하다보니 커스텀 파일을 사용해야될 필요가 있어 자료를 서칭해봤지만 적절한 한글 자료는 없어 엉성하기는하나 본인이 활용한 선에서 정리해보았다. 따지고보면 커스텀 파일을 얼마나 많이 만들고 활용하겠냐마는 그래도 알아두고 있다보면 언젠가 또 쓸 일이 있지 않을까 생각한다.