iOS

Core Transferable 톺아보기

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

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

이번 포스팅에서는 Core Transferable에 대해 학습해보겠습니다 🙋🏻

 

이전 Transferable 포스팅에서 Transferable이 어떤 프로토콜이고 실제로 어떻게 쓰이는지 알아봤는데요.

 

Transferable 찍먹하기

안녕하세요. 그린입니다 🍏 이번 포스팅에서는 이전 SwiftUI의 ShareLink를 학습하면서 나온 Transferable이라는 프로토콜에 대해 학습해보려고 합니다 🙋🏻 어떻게 이 주제에 대해 얘기가 나왔는지

green1229.tistory.com

 

이번에는 조금 더 상위로 올라가서 개념 자체와 좀 더 전송 유형에 세분화된것들을 알아보려고 합니다!

 

그럼 바로 들어가보겠습니다 🚀


Core Transferable

Core Transferable은 Transferable과 당연히 마찬가지로 WWDC 2022에서 나온 개념이기에 iOS 16.0 이상 버전에서 사용이 가능합니다.

Core Transferable은 데이터 전송 및 공유 컨텍스트에서 타입을 사용할 수 있도록 하는 Swift 중심 접근 방식을 제공해줍니다.

버튼을 통한 공유, 복사 & 붙여넣기, 드래그 & 들보 등 데이터를 이동하거나 공유하는 시스템 상호 작용에 전송 가능한 타입을 사용하는것이죠.

 

핵심 프로토콜인 Transferable을 앱의 모델에 채택하여 Core Transferable을 정의합니다.

하나 이상의 프레임워크 내장 TransferRepresentation 타입을 조합하여 전송 표현을 제공하죠.

그렇기에 Transferable 유형을 SwiftUI와 같은 시스템 프레임워크와 함께 사용하면, ShareLink / PasteButton와 같은 컴포넌트를 포함해 전송 가능한 데이터를 공유하고 붙여넣을 수 있는것이죠!

 

SwiftUI 또한,  draggable(_:) 및 dropDestination(for:action:isTargeted:)과 같은 뷰 모디파이어를 포함해 Transferable을 사용하는 드래그 & 드롭 인터랙션을 지원해줍니다.

String, Data, URL, Image와 같은 시스템 유형 자체는 이미 Transferable을 준수하고 있어요.

 

저번 포스팅에서도 이 내용을 다뤘지만 조금 더 명확히 개념적인 설명이 추가되어 이해가 잘간다면 성공입니다 😃

 

Transferable 아이템을 앱 내부, 여러 자신의 앱들 간 혹은 알려진 데이터 형식을 임포트하거나 익스포트하는 방법을 공유해 다른 앱들과 전송하거나 받을 수 있습니다.

 

실제로, Transferable 프로토콜을 준수하는 Note 모델 타입의 코드를 볼까요?

 

struct Note: Codable {
    var text: String
    var url: URL


    init(url: URL) {
        self.url = url
        self.text = ""
    }
}

extension Note: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(contentType: .note)
        ProxyRepresentation(exporting: \.text)
        FileRepresentation(
            contentType: .utf8PlainText,
            exporting: { note in SentTransferredFile(note.url) },
            importing: { received in
                let destination = URL(fileURLWithPath: <# ... #>)
                try FileManager.default.copyItem(at: received.file, to: destination)
                return Self.init(url: destination) })
        }
}

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

 

이전 포스팅에서도 이미 다뤘기에 어려움이 없는 코드일거에요.

한번 리마인드 차원에서 보면 좋을것 같습니다 😀

 

Core Tranferable을 Uniform Type Identifiers 프레임워크와 함께 사용하면, 표준 파일 형식 혹은 사용자 정의 파일 형식을 사용해 데이터를 이동하고 공유하는 시스템 상호 작용을 활용할 수 있어요.

Uniform Type Identifiers 프레임워크는 공통 파일 및 데이터 전송 식별자의 컬렉션을 제공해 여러 앱 간 파일 및 데이터 호환성을 실현시켜줍니다.

 

즉, Core Transferable과 Uniform Type Identifiers을 사용해 앱 내 / 앱 간 데이터 전송 시 표준화된 방식으로 데이터 타입을 정의하고 처리할 수 있는것이죠.

예시로, 문서나 이미지, 비디오 등의 파일 타입을 Core Transferable을 통해 전송하고자 한다면, Uniform Type Identifiers을 사용해 해당 파일 타입을 명확히 식별하도록 하고 다른 앱에서도 해당 파일을 정확히 인식하고 처리할 수 있게 되는것입니다.

 

이런 방식 자체가 앱 내부 혹은 앱 간 데이터 공유나 전송 시 안정적이고 효율적인 방법을 제공해주죠.

 

자 이렇게 Core Transferable의 개념 자체를 알아봤는데요!

 

이 포스팅에서 추가로 살펴볼건, 전송 표현 종류 및 방식들을 톺아보고 가려합니다 🙋🏻


Tranfer Type

 

전송 타입들을 보면 이렇게 세파트로 나눠져있긴 합니다.

 

1️⃣ 데이터 전송

2️⃣ 파일 전송

3️⃣ 커스텀 전송

 

이 세가지에 어떤것들이 있고, 개념은 어떤건지 톺아볼께요.


Data Transfer

1️⃣ CodableRepresentation

인코딩 및 디코딩 프로토콜을 채택한 타입을 위한 전송 표현입니다.

즉, Codable을 채택하고 있으면 되는것이죠.

 

struct CodableRepresentation<Item, Encoder, Decoder> where Item : Transferable, Item : Decodable, Item : Encodable, Encoder : TopLevelEncoder, Decoder : TopLevelDecoder, Encoder.Output == Data, Decoder.Input == Data

 

정의는 이러하며 사용은 아래와 같습니다.

 

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


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

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

 

Todo라는 Codable한 모델 자체를 전송할 수 있도록 사용하는 것이죠.

 


2️⃣ DataRepresentation

자체 바이너리 데이터 변환을 제공하는 유형에 대한 전송 표현입니다.

 

struct DataRepresentation<Item> where Item : Transferable

 

정의는 이러하며 사용은 아래와 같아요.

 

struct ImageDocumentLayer {
    init(data: Data) throws
    func data() -> Data
    func pngData() -> Data
}

extension ImageDocumentLayer: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        DataRepresentation(contentType: .layer) { layer in
                layer.data()
            } importing: { data in
                try ImageDocumentLayer(data: data)
            }
        DataRepresentation(exportedContentType: .png) { layer in
            layer.pngData()
        }
    }
}

 

이전 포스팅에서 저희가 만든 커스텀한 ImageDocumentLayer의 Transferable 사용 예시와 같아요!

어떻게보면 해당 모델이 Codable 프로토콜을 준수한다면 DataRepresentation보다 CodableRepresentation이 더 적합할 수 있겠네요!

 


File Transfer

1️⃣ FileRepresentation

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

 

struct FileRepresentation<Item> where Item : Transferable

 

정의는 이러하며, 사용은 아래와 같습니다.

 

struct Movie: Transferable {
    let url: URL
    static var transferRepresentation: some TransferRepresentation {
        FileRepresentation(contentType: .mpeg4Movie) { movie in
            SentTransferredFile($0.url)
            } importing: { received in
                let copy: URL = URL(fileURLWithPath: "<#...#>")
                try FileManager.default.copyItem(at: received.file, to: copy)
                return Self.init(url: copy) }
    }
}

 

즉, 데이터를 파일로 전달하는것이 효율적일때 사용하죠.

전송 가능한 항목을 파일로 가져오고 내보낼 수 있습니다.

SentTransferredFile도 바로 알아볼거에요ㅎㅎ

 


2️⃣ SentTransferredFile

발신자 관점에서 파일에 대한 설명입니다.

즉, FileRepresentation에서 같이 사용되죠!

file과 allowAccessingOriginalFile 프로퍼티를 가지고 사용할 수 있습니다.

 

struct SentTransferredFile

 


3️⃣ ReceivedTransferredFile

SentTransferredFile과 반대로 수신자 관점에서 파일에 대한 설명을 나타냅니다.

 

struct ReceivedTransferredFile

 

file과 isOriginalFile 프로퍼티를 가지고 사용할 수 있습니다.

 


Transfer Customization

1️⃣ ProxyRepresentation

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

 

struct ProxyRepresentation<Item, ProxyRepresentation> where Item : Transferable, ProxyRepresentation : Transferable

 

정의는 이러하며, 예시는 아래와 같아요.

 

struct Note: Transferable {
    var body: String


    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation(\.body)
    }
}

 

이럴때 Note 자체가 이미 Transferable을 채택하여 따르고 있는데, 여기 프로퍼티인 body를 전송하고자 할때 사용할 수 있죠.

 

하나 더 예시를 볼까요?

 

 struct Todo: Transferable, Codable {
    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") }
 }

 

Todo는 CodableRepresentation으로 표현되지만, 여기서 text를 보낼때는 ProxyRepresentation으로 표현해야 합니다.

여기서도 중첩되어 표현을 사용할때 더 선호되는 표현부터 작성해야 합니다 🙏🏻

 


2️⃣ TransferRepresentationVisibility

전송중 항목을 볼 수 있는 앱 및 프로세스의 종류를 지정하는 가시성 수준을 나타냅니다.

 

struct TransferRepresentationVisibility

 

해당 static 상수로 4가지가 존재합니다.

 

all - 모든 앱 또는 프로세스가 항목에 액세스 할 수 있음

team - 현재 앱 개발팀에서 만든 앱에만 표시되도록 지정

group - 동일한 앱 그룹에 있는 macOS 앱에만 표시되도록 지정

ownProcess - 앱 내에서만 표시되도록 지정

 


마무리

이렇게 한번 조금 더 파고 들어서 상위 개념인 Core Transferable을 톺아봤습니다!

iOS 16부터 사용이라 사실상 미니멈 타겟이 좀 높지 않으면 당장 사용하긴 버겁겠지만... 그래도 알면 좋죠ㅎㅎ

 


레퍼런스

 

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