🐙

プロトコルTransferableへの理解

2022/09/23に公開

Transferable

TransferableはiOS 16から利用可能なプロトコルです。
その名の通り転送可能であることを示すプロトコルです。

転送可能になることでDrag & DropなどのUI上でアイテムを転送可能にしたりすることができます。

他にもPhotosPickerでTransferableが使用されています。

Apple Transferable

https://zenn.dev/zunda_pixel/articles/7dd3a47b0a3998

https://zenn.dev/zunda_pixel/articles/0be43b8b4b36b5

Transferableへの理解

Transferableは難しそうですが、ほぼCodableと一緒です。
モデル -> DataData -> モデルへの変換が用意されていれば問題ないです。

実装例(CodableRepresentation)(Codable可能)

struct Person: Codable {
  let age: Int
  let name: String
}

extension Person: Transferable {
  static var transferRepresentation: some TransferRepresentation {
    CodableRepresentation(for: Person.self, contentType: .person)
  }
}

// Info.plistに独自のUTTypeを登録する必要がある
// 適当にUTType.contentとかを使っても問題ない
extension UTType {
  static var person: UTType { UTType(exportedAs: "com.example.person") }
}

実装例(DataRepresentation)

自前でモデル -> DataData -> モデルの変換処理を実装

struct ImageModel {
  let image: UIImage
}

extension ImageModel: Transferable {
  static var transferRepresentation: some TransferRepresentation {
    DataRepresentation(contentType: .content) { imageModel in
      return imageModel.image.pngData()!
    } importing: { data in
      let uiImage = UIImage(data: data)!
      return ImageModel(image: uiImage)
    }
  }
}

extension UTType {
  static var imageModel: UTType { UTType(exportedAs: "com.sample.imageModel") }
}

実装例(FileRepresentation)

Dataで転送(Transfer)するには重すぎるようなモデル(動画など)では、ファイルパスのみを転送することで簡単にTransferableに準拠させることができます。

struct Movie {
  let url: URL
}

extension Movie: Transferable {
  static var transferRepresentation: some TransferRepresentation {
    FileRepresentation(contentType: .movie) { movie in
      SentTransferredFile(movie.url)
    } importing: { receivedData in
      let fileName = receivedData.file.lastPathComponent
      let copy: URL = FileManager.default.temporaryDirectory.appendingPathComponent(fileName)
      try FileManager.default.copyItem(at: receivedData.file, to: copy)
      return .init(url: copy)
    }
  }
}

extension UTType {
  static var movie: UTType { UTType(exportedAs: "com.sample.movie") }
}

実装例(ProxyRepresentation)

すでにTransferableに準拠ずみのプロパティを設定可能です。

struct Note: Transferable {
  var body: String
  
  static var transferRepresentation: some TransferRepresentation {
    ProxyRepresentation(exporting: \.body)
  }
}

Discussion