117 lines
4.9 KiB
Swift
117 lines
4.9 KiB
Swift
//
|
|
// CompositionAttachment.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 3/14/20.
|
|
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import UIKit
|
|
import MobileCoreServices
|
|
|
|
final class CompositionAttachment: NSObject, Codable, ObservableObject {
|
|
static let typeIdentifier = "space.vaccor.Tusker.composition-attachment"
|
|
|
|
let id: UUID
|
|
@Published var data: CompositionAttachmentData
|
|
@Published var attachmentDescription: String
|
|
|
|
init(data: CompositionAttachmentData, description: String = "") {
|
|
self.id = UUID()
|
|
self.data = data
|
|
self.attachmentDescription = description
|
|
}
|
|
|
|
init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
|
|
self.id = try container.decode(UUID.self, forKey: .id)
|
|
self.data = try container.decode(CompositionAttachmentData.self, forKey: .data)
|
|
self.attachmentDescription = try container.decode(String.self, forKey: .attachmentDescription)
|
|
}
|
|
|
|
func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
|
|
try container.encode(id, forKey: .id)
|
|
try container.encode(data, forKey: .data)
|
|
try container.encode(attachmentDescription, forKey: .attachmentDescription)
|
|
}
|
|
|
|
static func ==(lhs: CompositionAttachment, rhs: CompositionAttachment) -> Bool {
|
|
return lhs.id == rhs.id
|
|
}
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case data
|
|
case attachmentDescription
|
|
}
|
|
}
|
|
|
|
extension CompositionAttachment: Identifiable {}
|
|
|
|
private let imageType = kUTTypeImage as String
|
|
private let mp4Type = kUTTypeMPEG4 as String
|
|
private let quickTimeType = kUTTypeQuickTimeMovie as String
|
|
private let dataType = kUTTypeData as String
|
|
|
|
extension CompositionAttachment: NSItemProviderWriting {
|
|
static var writableTypeIdentifiersForItemProvider: [String] {
|
|
[typeIdentifier]
|
|
}
|
|
|
|
func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? {
|
|
if typeIdentifier == CompositionAttachment.typeIdentifier {
|
|
do {
|
|
completionHandler(try PropertyListEncoder().encode(self), nil)
|
|
} catch {
|
|
completionHandler(nil, error)
|
|
}
|
|
}
|
|
|
|
completionHandler(nil, ItemProviderError.incompatibleTypeIdentifier)
|
|
return nil
|
|
}
|
|
|
|
enum ItemProviderError: Error {
|
|
case incompatibleTypeIdentifier
|
|
|
|
var localizedDescription: String {
|
|
switch self {
|
|
case .incompatibleTypeIdentifier:
|
|
return "Cannot provide data for given type"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extension CompositionAttachment: NSItemProviderReading {
|
|
static var readableTypeIdentifiersForItemProvider: [String] {
|
|
// todo: is there a better way of handling movies than manually adding all possible UTI types?
|
|
// just using kUTTypeMovie doesn't work, because we need the actually type in order to get the file extension
|
|
// without the file extension, getting the thumbnail and exporting the video for attachment upload fails
|
|
[typeIdentifier] + UIImage.readableTypeIdentifiersForItemProvider + [mp4Type, quickTimeType] + NSURL.readableTypeIdentifiersForItemProvider
|
|
}
|
|
|
|
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Self {
|
|
if typeIdentifier == CompositionAttachment.typeIdentifier {
|
|
return try PropertyListDecoder().decode(Self.self, from: data)
|
|
} else if UIImage.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let image = try? UIImage.object(withItemProviderData: data, typeIdentifier: typeIdentifier) {
|
|
return CompositionAttachment(data: .image(image)) as! Self
|
|
} else if typeIdentifier == mp4Type || typeIdentifier == quickTimeType {
|
|
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
|
let temporaryFileName = ProcessInfo().globallyUniqueString
|
|
let fileExt = UTTypeCopyPreferredTagWithClass(typeIdentifier as CFString, kUTTagClassFilenameExtension)!
|
|
let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(temporaryFileName).appendingPathExtension(fileExt.takeUnretainedValue() as String)
|
|
try data.write(to: temporaryFileURL)
|
|
return CompositionAttachment(data: .video(temporaryFileURL)) as! Self
|
|
} else if NSURL.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let url = try? NSURL.object(withItemProviderData: data, typeIdentifier: typeIdentifier) as URL {
|
|
return CompositionAttachment(data: .video(url)) as! Self
|
|
} else {
|
|
throw ItemProviderError.incompatibleTypeIdentifier
|
|
}
|
|
}
|
|
}
|