159 lines
5.5 KiB
Swift
159 lines
5.5 KiB
Swift
//
|
|
// AttachmentViewModel+Load.swift
|
|
//
|
|
//
|
|
// Created by MainasuK on 2022/11/8.
|
|
//
|
|
|
|
import UIKit
|
|
import AVKit
|
|
import UniformTypeIdentifiers
|
|
import Nuke
|
|
|
|
extension AttachmentViewModel {
|
|
|
|
@MainActor
|
|
func load(input: Input) async throws -> Output {
|
|
switch input {
|
|
case .image(let image):
|
|
guard let data = image.normalized()?.pngData() else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
return .image(data, imageKind: .png)
|
|
case .url(let url):
|
|
do {
|
|
let output = try await AttachmentViewModel.load(url: url)
|
|
return output
|
|
} catch {
|
|
throw error
|
|
}
|
|
case .mastodonAssetUrl(let url, _):
|
|
return try await Self.loadMastodonAsset(url: url)
|
|
case .pickerResult(let pickerResult):
|
|
do {
|
|
let output = try await AttachmentViewModel.load(itemProvider: pickerResult.itemProvider)
|
|
return output
|
|
} catch {
|
|
throw error
|
|
}
|
|
case .itemProvider(let itemProvider):
|
|
do {
|
|
let output = try await AttachmentViewModel.load(itemProvider: itemProvider)
|
|
return output
|
|
} catch {
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
private static func loadMastodonAsset(url: URL) async throws -> Output {
|
|
guard !url.isFileURL else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
let (imageData, _) = try await URLSession.shared.data(from: url)
|
|
return .image(imageData, imageKind: AssetType(imageData) == .png ? .png : .jpg)
|
|
}
|
|
|
|
private static func load(url: URL) async throws -> Output {
|
|
guard let uti = UTType(filenameExtension: url.pathExtension) else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
|
|
if uti.conforms(to: .image) {
|
|
guard url.startAccessingSecurityScopedResource() else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
defer { url.stopAccessingSecurityScopedResource() }
|
|
let imageData = try Data(contentsOf: url)
|
|
return .image(imageData, imageKind: AssetType(imageData) == .png ? .png : .jpg)
|
|
} else if uti.conforms(to: .movie) {
|
|
guard url.startAccessingSecurityScopedResource() else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
defer { url.stopAccessingSecurityScopedResource() }
|
|
|
|
let fileName = UUID().uuidString
|
|
let tempDirectoryURL = FileManager.default.temporaryDirectory
|
|
let fileURL = tempDirectoryURL.appendingPathComponent(fileName).appendingPathExtension(url.pathExtension)
|
|
try FileManager.default.createDirectory(at: tempDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
|
try FileManager.default.copyItem(at: url, to: fileURL)
|
|
return .video(fileURL, mimeType: UTType.movie.preferredMIMEType ?? "video/mp4")
|
|
} else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
}
|
|
|
|
private static func load(itemProvider: NSItemProvider) async throws -> Output {
|
|
if itemProvider.isImage() {
|
|
guard let result = try await itemProvider.loadImageData() else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
let imageKind: Output.ImageKind = {
|
|
if let type = result.type {
|
|
if type == UTType.png {
|
|
return .png
|
|
}
|
|
if type == UTType.jpeg {
|
|
return .jpg
|
|
}
|
|
}
|
|
|
|
let imageData = result.data
|
|
let assetType = AssetType(imageData)
|
|
|
|
if assetType == .png {
|
|
return .png
|
|
}
|
|
if assetType == .jpeg {
|
|
return .jpg
|
|
}
|
|
|
|
assertionFailure("unknown image kind")
|
|
return .jpg
|
|
}()
|
|
return .image(result.data, imageKind: imageKind)
|
|
} else if itemProvider.isMovie() {
|
|
guard let result = try await itemProvider.loadVideoData() else {
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
return .video(result.url, mimeType: "video/mp4")
|
|
} else {
|
|
assertionFailure()
|
|
throw AttachmentError.invalidAttachmentType
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension AttachmentViewModel {
|
|
static func createThumbnailForVideo(url: URL) -> UIImage? {
|
|
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
|
|
let asset = AVURLAsset(url: url)
|
|
let assetImageGenerator = AVAssetImageGenerator(asset: asset)
|
|
assetImageGenerator.appliesPreferredTrackTransform = true // fix orientation
|
|
do {
|
|
let cgImage = try assetImageGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
|
|
let image = UIImage(cgImage: cgImage)
|
|
return image
|
|
} catch {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
extension NSItemProvider {
|
|
func isImage() -> Bool {
|
|
return hasRepresentationConforming(
|
|
toTypeIdentifier: UTType.image.identifier,
|
|
fileOptions: []
|
|
)
|
|
}
|
|
|
|
func isMovie() -> Bool {
|
|
return hasRepresentationConforming(
|
|
toTypeIdentifier: UTType.movie.identifier,
|
|
fileOptions: []
|
|
)
|
|
}
|
|
}
|