iOS

Transferable 찍먹하기

GREEN.1229 2024. 4. 15. 18:59

안녕하세요. 그린입니다 🍏

이번 포스팅에서는 이전 SwiftUI의 ShareLink를 학습하면서 나온 Transferable이라는 프로토콜에 대해 학습해보려고 합니다 🙋🏻

 

어떻게 이 주제에 대해 얘기가 나왔는지 아래 포스팅을 참고하면 알 수 있습니다!
 

SwiftUI - ShareLink

안녕하세요. 그린입니다 🍏 이번 포스팅에서는 SwiftUI의 ShareLink에 대해 알아보겠습니다 🙋🏻 사실 예전 포스팅에서 SwiftUI에서 공유 시트를 위한 ActivityView를 사용하는것을 학습한적이 있습니

green1229.tistory.com

 

그럼 한번 알아볼까요?


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에 해당 항목을 포함해야 합니다.

자세히는 앱의 파일 및 데이터 유형 정의 가이드 문서를 살펴보면 좋을것 같네요!

 

Defining file and data types for your app | Apple Developer Documentation

Declare uniform type identifiers to support your app’s proprietary data formats.

developer.apple.com

 

또, 만약 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을 담아 전송할 수 없었던것이 이제는 가능해진것이죠.

 


마무리

이번 포스팅에서는 찍먹 정도만 가볍게 톺아봤는데, 좀 더 심화적으로 볼 부분이 많더라구요!

그래서 다음번에 좀 더 심화적인 요 학습을 해볼까 합니다.

 

Core Transferable | Apple Developer Documentation

Declare a transfer representation for your model types to participate in system sharing and data transfer operations.

developer.apple.com

 


레퍼런스

 

Transferable | Apple Developer Documentation

A protocol that describes how a type interacts with transport APIs such as drag and drop or copy and paste.

developer.apple.com

 

 

TransferRepresentation | Apple Developer Documentation

A declarative description of the process of importing and exporting a transferable item.

developer.apple.com