Transferable 찍먹하기
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 이전 SwiftUI의 ShareLink를 학습하면서 나온 Transferable이라는 프로토콜에 대해 학습해보려고 합니다 🙋🏻
어떻게 이 주제에 대해 얘기가 나왔는지 아래 포스팅을 참고하면 알 수 있습니다!
그럼 한번 알아볼까요?
Transferable
우선, WWDC 2022에서 소개된 프로토콜로 iOS 16.0 이상에서 채택하여 사용할 수 있어요!
정의 자체는 타입이 드래그 앤 드롭 혹은 복사 및 붙여넣기와 같은 전송 API와 상호작용하는 방법을 설명하는 프로토콜입니다.
protocol Transferable
심플한 프로토콜같죠?
쉽게 생각해보면 ShareLink를 예시로 떠올려봐도 좋아요.
앱 사이 소통할 수 있는 타입으로 만들기 위해 채택하는것이죠.
ShareLink에서 String, Image, Data, URL 등을 보낼 수 있었던것처럼 해당 타입들은 모두 Transferable 프로토콜을 채택하고 있습니다.
Transferable 프로토콜을 준수하기 위해서는 transferRepresentation 프로퍼티를 구현해야 합니다.
좀 더 자세히 알아보기위해, 공식문서의 예시 코드와 함께 살펴볼께요!
struct ImageDocumentLayer {
init(data: Data)
func data() -> Data
func pngData() -> Data
}
해당 코드를 살펴보자면, 이미지 편집 앱의 레이어 타입은 사람들이 문서 내에서 이미지 레이어의 순서를 변경하기 위해 이미지 레이어를 드래그 앤 드롭할 수 있도록 Transferable을 준수하여 구현할 수 있습니다.
즉, 해당 ImageDocumentLayer에 Transferable 프로토콜을 채택하여 구현을 할 수 있다는것이죠.
extension ImageDocumentLayer: Transferable {
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(contentType: .layer) { layer in
layer.data()
}, importing: { data in
try Layer(data: data)
}
DataRepresentation(exportedContentType: .png) { layer in
layer.pngData()
}
}
이렇게 확장하여 채택한 후, 필수인 transferRepresentation 프로퍼티를 구현해주는것입니다.
두개로 구성을 해줬는데요.
앱 내 레이어 혹은 사용자 지정 레이어 콘텐츠 타입을 인식하는 다른 앱으로 드래그 앤 드롭을 한다면 해당 앱은 첫번째 DataRepresentation의 구현을 통해 사용합니다.
만약 다른 이미지 편집기에 레이어를 드래그 앤 드롭하면 편집기가 PNG 파일 타입을 인식할 수 있기에 두번째 DataRepresentation에서 PNG 파일에 대한 지원 구현을 추가한것이죠.
extension UTType {
static var layer: UTType { UTType(exportedAs: "com.example.layer") }
}
해당 코드로 커스텀한 레이어 타입 식별자를 선언해줌으로 사용을 하죠.
주의할점은, 앱에서 해당 커스텀한 사용자 지정 타입 식별자를 선언할때는 앱의 Info.plist에 해당 항목을 포함해야 합니다.
자세히는 앱의 파일 및 데이터 유형 정의 가이드 문서를 살펴보면 좋을것 같네요!
또, 만약 Codable을 준수하는 타입이 있다면 Transferable은 자동으로 데이터로의 변환 및 데이터로부터의 변환을 처리해줍니다.
struct Note: Codable {
let title: String
let body: String
}
extension Note: Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .note)
}
}
CodableRepresentation을 이용하여 해당 타입을 전송가능하게 만들어주죠.
커스텀한 Note 식별자에 대해 모르는 다른 앱과의 호환성을 보장하기 위해서 Note를 title 텍스트로 변환하는 추가 구현을 해야 합니다.
extension Note: Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .note)
ProxyRepresentation(\.title)
}
}
이런식으로 말이죠!
여기서 Representation의 순서가 중요해요.
Note라는 타입 자체를 표현하는 Representation을 가장 먼저 선언하고, 호환 가능하지만 덜 선호되는 Representation을 순서대로 선언해주면 됩니다.
간단히 이렇게 소개할 수 있겠는데요.
앞서 나왔던 필수적인 transferRepresentation 프로퍼티는 항목을 가져오고 내보내는데 사용되는 표현입니다.
@TransferRepresentationBuilder<Self> static var transferRepresentation: Self.Representation { get }
이렇게, 선언이 되어 있는데요.
파고 들어가보면 타입인 Representation은 항목을 가져오고 내보내는데 사용하는 표현 그자체 유형이죠.
associatedtype Representation : TransferRepresentation
TrasnferRepresntation의 associatedType으로 해당 TrasnferRepresntation을 살펴볼께요!
TrasnferRepresntation?
전송 가능한 항목을 가져오고 내보내는 과정에 대한 선언적인 프로토콜입니다.
protocol TransferRepresentation<Item> : Sendable
앞서 살펴봤듯 여러 전송 표현 자체를 결합해서 단일 전송 표현을 구성할 수 있습니다.
import UniformTypeIdentifiers
struct Greeting: Codable, Transferable {
let message: String
var displayInAllCaps: Bool = false
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .greeting)
ProxyRepresentation(exporting: \.message)
}
}
extension UTType {
static var greeting: UTType { .init(exportedAs: "com.example.greeting") }
}
message 문자열을 통해서 Codable 타입과 Proxy로 전송하는 Greeting 타입을 보여주고 있습니다.
그래서 어떻게 실제적으로 쓰이냐!?
이전 학습한 포스팅에서 예시를 가져와볼께요.
struct Photo: Transferable {
static var transferRepresentation: some TransferRepresentation {
ProxyRepresentation(exporting: \.image)
}
public var image: Image
public var caption: String
}
이렇게 Photo 타입을 선언할때 Transferable 프로토콜을 채택하고 transferRepresentation을 구현해줍니다.
이때 ProxyRepresentation으로 이미지 자체를 익스포팅할 수 있도록 구성하죠.
struct PhotoView: View {
let photo: Photo
var body: some View {
photo.image
.toolbar {
ShareLink(
item: photo,
preview: SharePreview(
photo.caption,
image: photo.image
)
)
}
}
그리고, ShareLink를 통해 해당 이미지 데이터를 전달할 수 있도록 사용하기만 하면 되는것이죠!
기존에는 photo 자체가 Transferable하지 않았기에 ShareLink를 띄워도 이미지 item을 담아 전송할 수 없었던것이 이제는 가능해진것이죠.
마무리
이번 포스팅에서는 찍먹 정도만 가볍게 톺아봤는데, 좀 더 심화적으로 볼 부분이 많더라구요!
그래서 다음번에 좀 더 심화적인 요 학습을 해볼까 합니다.
레퍼런스