This commit is contained in:
fmodf 2024-07-10 21:57:45 +02:00
parent ec7b075b35
commit ad8f4be1c7
2 changed files with 185 additions and 92 deletions

View file

@ -7,7 +7,6 @@ import UIKit
final class SharingMiddleware { final class SharingMiddleware {
static let shared = SharingMiddleware() static let shared = SharingMiddleware()
// swiftlint:disable:next function_body_length
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> { func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
switch action { switch action {
case .sharingAction(.checkCameraAccess): case .sharingAction(.checkCameraAccess):
@ -54,104 +53,200 @@ final class SharingMiddleware {
case .sharingAction(.fetchGallery): case .sharingAction(.fetchGallery):
return Future<AppAction, Never> { promise in return Future<AppAction, Never> { promise in
DispatchQueue.global(qos: .background).async {
let fetchOptions = PHFetchOptions() let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
let assets = PHAsset.fetchAssets(with: fetchOptions) let assets = PHAsset.fetchAssets(with: fetchOptions)
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
option.isSynchronous = true
var items: [SharingGalleryItem] = [] var items: [SharingGalleryItem] = []
let group = DispatchGroup()
assets.enumerateObjects { asset, _, _ in assets.enumerateObjects { asset, _, _ in
if asset.mediaType == .image { if asset.mediaType == .image {
group.enter()
manager.requestImage(
for: asset,
targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFill,
options: option
) { image, _ in
if image != nil {
items.append(.init(id: asset.localIdentifier, type: .photo)) items.append(.init(id: asset.localIdentifier, type: .photo))
group.leave()
}
}
} else if asset.mediaType == .video { } else if asset.mediaType == .video {
group.enter() items.append(.init(id: asset.localIdentifier, type: .video))
manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
if avAsset != nil {
items.append(.init(id: asset.localIdentifier, type: .video, duration: asset.duration.minAndSec))
group.leave()
} }
} }
}
}
group.notify(queue: .main) {
promise(.success(.sharingAction(.galleryFetched(items)))) promise(.success(.sharingAction(.galleryFetched(items))))
} }
}
}
.eraseToAnyPublisher() .eraseToAnyPublisher()
case .sharingAction(.galleryFetched(let items)): case .sharingAction(.galleryFetched(let items)):
return Future<AppAction, Never> { promise in
DispatchQueue.global(qos: .background).async { DispatchQueue.global(qos: .background).async {
let ids = items // for item in items {
.filter { $0.thumbnail == nil } // switch item.type {
.map { $0.id } // case .photo:
let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil) // self.makePhotoThumbnail(assetId: PHAsset.fetchAssets(withLocalIdentifiers: [item.id], options: nil).firstObject!) { data in
assets.enumerateObjects { asset, _, _ in // if let data {
let manager = PHImageManager.default() // DispatchQueue.main.async {
if asset.mediaType == .image { // store.dispatch(.sharingAction(.thumbnailUpdated(data, item.id)))
manager.requestImage( // }
for: asset, // }
targetSize: PHImageManagerMaximumSize, // }
contentMode: .aspectFill, //
options: nil // case .video:
) { image, _ in // self.fetchVideo(asset: PHAsset.fetchAssets(withLocalIdentifiers: [item.id], options: nil).firstObject!) { newItem in
image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in // if let newItem {
if let image { // newItems.append(newItem)
let data = image.jpegData(compressionQuality: 1.0) ?? Data() // }
DispatchQueue.main.async { // }
store.dispatch(.sharingAction(.thumbnailUpdated(data, asset.localIdentifier))) // }
// }
} }
} return Empty().eraseToAnyPublisher()
}
}
} else if asset.mediaType == .video {
manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
if let avAsset {
let imageGenerator = AVAssetImageGenerator(asset: avAsset)
imageGenerator.appliesPreferredTrackTransform = true
let time = CMTimeMake(value: 1, timescale: 2)
do {
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
let thumbnail = UIImage(cgImage: imageRef)
thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize), completion: { image in
if let image {
let data = image.jpegData(compressionQuality: 1.0) ?? Data()
DispatchQueue.main.async {
store.dispatch(.sharingAction(.thumbnailUpdated(data, asset.localIdentifier)))
}
}
})
} catch {
print("Failed to create thumbnail image")
}
}
}
}
}
}
promise(.success(.empty))
}
.eraseToAnyPublisher()
default: default:
return Empty().eraseToAnyPublisher() return Empty().eraseToAnyPublisher()
} }
} }
} }
private extension SharingMiddleware {
func makePhotoThumbnail(asset: PHAsset, completion: @escaping (Data?) -> Void) {
PHImageManager.default().requestImage(
for: asset,
targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFill,
options: nil
) { _, _ in
// guard let image = image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) else {
// completion(nil)
// return
// }
// let data = image.jpegData(compressionQuality: 1.0) ?? Data()
// completion(.init(id: asset.localIdentifier, type: .photo, thumbnail: data))
completion(.init(id: asset.localIdentifier, type: .photo, thumbnail: Data()))
}
}
func fetchVideo(asset: PHAsset, completion: @escaping (SharingGalleryItem?) -> Void) {
PHImageManager.default().requestAVAsset(forVideo: asset, options: nil) { _, _, _ in
// if let avAsset {
// let imageGenerator = AVAssetImageGenerator(asset: avAsset)
// imageGenerator.appliesPreferredTrackTransform = true
// let time = CMTimeMake(value: 1, timescale: 2)
// do {
// let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
// let thumbnail = UIImage(cgImage: imageRef)
// guard let image = thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) else {
// completion(nil)
// return
// }
// let data = image.jpegData(compressionQuality: 1.0) ?? Data()
// completion(.init(id: asset.localIdentifier, type: .video, thumbnail: data, duration: asset.duration.minAndSec))
// } catch {
// print("Failed to create thumbnail image")
// completion(nil)
// }
// } else {
// completion(nil)
// }
completion(.init(id: asset.localIdentifier, type: .photo, thumbnail: Data()))
}
}
}
// case .sharingAction(.fetchGallery):
// return Future<AppAction, Never> { promise in
// DispatchQueue.global(qos: .background).async {
// let fetchOptions = PHFetchOptions()
// fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
// let assets = PHAsset.fetchAssets(with: fetchOptions)
//
// let manager = PHImageManager.default()
// let option = PHImageRequestOptions()
// option.isSynchronous = true
//
// var items: [SharingGalleryItem] = []
// let group = DispatchGroup()
// assets.enumerateObjects { asset, _, _ in
// if asset.mediaType == .image {
// group.enter()
// manager.requestImage(
// for: asset,
// targetSize: PHImageManagerMaximumSize,
// contentMode: .aspectFill,
// options: option
// ) { image, _ in
// if image != nil {
// items.append(.init(id: asset.localIdentifier, type: .photo))
// group.leave()
// }
// }
// } else if asset.mediaType == .video {
// group.enter()
// manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
// if avAsset != nil {
// items.append(.init(id: asset.localIdentifier, type: .video, duration: asset.duration.minAndSec))
// group.leave()
// }
// }
// }
// }
// group.notify(queue: .main) {
// promise(.success(.sharingAction(.galleryFetched(items))))
// }
// }
// }
// .eraseToAnyPublisher()
//
// case .sharingAction(.galleryFetched(let items)):
// return Future<AppAction, Never> { promise in
// DispatchQueue.global(qos: .background).async {
// let ids = items
// .filter { $0.thumbnail == nil }
// .map { $0.id }
// let assets = PHAsset.fetchAssets(withLocalIdentifiers: ids, options: nil)
//
// var newItems: [SharingGalleryItem] = []
// let group = DispatchGroup()
//
// assets.enumerateObjects { asset, _, _ in
// let manager = PHImageManager.default()
// group.enter()
// if asset.mediaType == .image {
// manager.requestImage(
// for: asset,
// targetSize: PHImageManagerMaximumSize,
// contentMode: .aspectFill,
// options: nil
// ) { image, _ in
// image?.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize)) { image in
// if let image {
// let data = image.jpegData(compressionQuality: 1.0) ?? Data()
// newItems.append(.init(id: asset.localIdentifier, type: .photo, thumbnail: data))
// }
// group.leave()
// }
// }
// } else if asset.mediaType == .video {
// manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, _, _ in
// if let avAsset {
// let imageGenerator = AVAssetImageGenerator(asset: avAsset)
// imageGenerator.appliesPreferredTrackTransform = true
// let time = CMTimeMake(value: 1, timescale: 2)
// do {
// let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
// let thumbnail = UIImage(cgImage: imageRef)
// thumbnail.scaleAndCropImage(toExampleSize: CGSize(width: Const.galleryGridSize, height: Const.galleryGridSize), completion: { image in
// if let image {
// let data = image.jpegData(compressionQuality: 1.0) ?? Data()
// newItems.append(.init(id: asset.localIdentifier, type: .video, thumbnail: data, duration: asset.duration.minAndSec))
// }
// group.leave()
// })
// } catch {
// print("Failed to create thumbnail image")
// group.leave()
// }
// } else {
// group.leave()
// }
// }
// } else {
// group.leave()
// }
// }
// group.notify(queue: .main) {
// promise(.success(.sharingAction(.thumbnailUpdated(newItems))))
// }
// }
// }
// .eraseToAnyPublisher()

View file

@ -36,9 +36,7 @@ extension UIImage {
} }
let image: UIImage = .init(cgImage: imgRef, scale: self.scale, orientation: self.imageOrientation) let image: UIImage = .init(cgImage: imgRef, scale: self.scale, orientation: self.imageOrientation)
DispatchQueue.main.async {
completion(image) completion(image)
} }
} }
}
} }