iOS+/Swift

Transferable

hot6 2024. 9. 8. 19:45

개요

이 글에서는 Transferable에 대해 자세히 정리한다

그리고 Drag&Drop 기능에서 Transferable이 사용되는 예시에 대해 알아본다

 

  • Transferable
  • Transfer Type
    • Data Transfer
    • File Transfer
    • Custom Transfer
  • Drag & Drop

 

Transferable

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

 

Transferable에 대한 문서에서의 설명은 위와 같고, String, Data, URL, Image와 같은 시스템 유형 자체는 이미 Transferable을 준수하고 있다

 

Transferable 프로토콜을 채택하기 위해선 transferRepresentation 프로퍼티를 구현해야한다

해당 프로퍼티는 TransferRepresentation을 반환하는데 이는 다음과 같다

 

@TransferRepresentationBuilder<Self> static var transferRepresentation: Self.Representation { get }

 

이름에 Builder가 들어가는 걸로 봐선, 배열을 안 쓰고 클로저 내에 그냥 여러 개의 Representation들을 넣는 방식으로 정의하면 된다는 것을 유추할 수 있다

 

Transfer Type

struct Todo: Codable, Transferable {
    var text: String
    var isDone = false

    static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(contentType: .todo)
        ProxyRepresentation(\\.text)
    }
}

 extension UTType {
     static var todo: UTType { UTType(exportedAs: "com.example.todo") }
}

 

Transferable를 채택하기 위해 transferRepresentation 프로퍼티 정의에 사용되는 타입들로는 다음과 같이 존재한다

 

  • Data Transfer
  • File Transfer
  • Custom Transfer

 

Data Transfer

contentType 인자에 사용될 UTType으로 정의해야 하고, Info.plist에 등록해야 한다

Data와 타입과의 변환 처리를 구현해야 한다고 생각할 수 있다

CodableRepresentation(contentType: UTType, encoder, decoder)

Codable 타입을 위한 전송표현으로, Data↔Item 전환과정이 Codable을 이용하여 처리한다

 

init(
    for itemType: Item.Type = Item.self,
    contentType: UTType,
    encoder: Encoder,
    decoder: Decoder
)

DataRepresentation(contentType: UTType, exporting, importing)

자체 바이너리 데이터 변환을 제공하는 유형에 대한 전송 표현으로, 정의한 데이터 Item 타입에서 Item→Data와 Data→Item 변환을 “직접” 정의한다

 

init(
    contentType: UTType,
    exporting: @escaping (Item) async throws -> Data,
    importing: @escaping (Data) async throws -> Item
)

 

File Transfer

FileRepresentation(contentType: UTType, shouldAttemptToOpenInplace, exporting, importing)

파일 URL로 전송되는 유형에 대한 전송 표현이다

 

init(
    contentType: UTType,
    shouldAttemptToOpenInPlace: Bool = false,
    exporting: @escaping (Item) async throws -> SentTransferredFile,
    importing: @escaping (ReceivedTransferredFile) async throws -> Item
)

 

기존에 Data로 처리하던 게 SentTransferedFileRecievedTransferredFile로 처리한다고 보면 된다

참고로 shouldAttemptToOpenInPlace는 기존요소를 그대로 이용하는가 아니면 복사하여 따로 이용하는 가에 대한 설정이다

 

  • SentTransferedFile
  • RecievedTransferredFile

 

Custom Transfer

ProxyRepresentation(exporting, importing)

다른 유형의 전송 표현을 자체적으로 사용하는 전송 표현이다

주로 특정 속성만 전송하고자 할 때 사용된다

 

init(
    exporting: @escaping (Item) throws -> ProxyRepresentation,
    importing: @escaping (ProxyRepresentation) async throws -> Item
)

 

Drag & Drop

.draggable(_: preview:)

nonisolated
func draggable<V, T>(
    _ payload: @autoclosure @escaping () -> T,
    @ViewBuilder preview: () -> V
) -> some View where V : View, T : Transferable

.dropDestination(for: action: isTargeted:)

nonisolated
func dropDestination<T>(
    for payloadType: T.Type = T.self,
    action: @escaping ([T], CGPoint) -> Bool,
    isTargeted: @escaping (Bool) -> Void = { _ in }
) -> some View where T : Transferable

 

.draggble은 Trasnferable을 준수하는 payload 인자를 수정자에 전달해야 하는데

이는 .dropDestination에서 payload로 전달받아 사용된다

 

즉, 어떤 draggable한 뷰를 drag해서 drop한다는 것은 preview로 이동되는 뷰가 효과로 나타남과 동시에 실제로 payload로 관련 데이터가 전달되어야 한다는 것을 의미한다

 

예시코드

위 예시코드에서 TodoItemView를 draggable로 설정하고, 이를 droppable하게 Group을 설정했다

 

마무리

Transferable을 사용한 커스텀 타입 정의를 통해 개발자는 자신의 앱에 맞는 전송 로직을 손쉽게 구현할 수 있고, 다른 앱과의 상호작용도 원활하게 처리할 수 있다

특히, Drag&Drop같은 기능과의 결합을 통해 사용자에게 직관적이고 유연한 데이터 이동 경험을 제공할 수 있다