🫠
UICollectionViewDropDelegateは壊れている
未来の自分と同じ問題に引っかかった誰かのための備忘録。
インターネットのどこにも書いていなかったので
UICollectionView
がDropを受け取る場合、次のUICollectionViewDropDelegate
の次のメソッドを定義してdropを受け取ります。
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator)
多くの場合次のようにしてアイテムを受け取ると考えられます。
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
for dropItem in coordinator.items {
let provider = dropItem.dragItem.itemProvider
// 受け取り処理
// provider.loadObject, provider.loadFileRepresantation など
}
}
しかしこの時、awaitを使うために次のようにTask
を使ったりDispatchQueue.main.async
を利用してこのメソッドのスコープから出ると全てのload*
系メソッドが壊れます。
アプリ内では壊れない、アプリ外からのDropで壊れる。
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
Task { // Contextを引き継ぐので@MainActor
for dropItem in coordinator.items {
let provider = dropItem.dragItem.itemProvider
// 受け取り処理
// provider.loadObject, provider.loadFileRepresantation などのメソッドが壊れ、completionHandlerは呼ばれず、awaitは無限に続く
}
}
}
例えばloadFileRepresentation
はcompletionHandler
を即座に呼び出さずに解放しますし、loadItem
は決して解決されないasync関数になってしまいます(いつまで待ってもawaitから先に行かない)
これの酷いのは
loadItem
はasync関数である(正確には自動Convertされたためcompletion handlerが非推奨になっている)のでTaskを作って呼び出す必要があるにも関わらず、Taskを作って呼び出すとメソッドから出ることになるので確定で壊れることです。
しかし、NSItemProvider.canLoad
やNSItemProvider.registeredContentTypes
は特に変化しないためこのことに気が付きにくくなっています。
AppleのDocumentにもメソッドから出ては行けない記載もなかったため、iOSのバグか暗黙の仕様と思われます。
以上
Discussion