// // PhotoCaptureDelegate.swift // mrousavy // // Created by Marc Rousavy on 15.12.20. // Copyright © 2020 mrousavy. All rights reserved. // import AVFoundation private var delegatesReferences: [NSObject] = [] // MARK: - PhotoCaptureDelegate class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate { private let promise: Promise private let enableShutterSound: Bool required init(promise: Promise, enableShutterSound: Bool) { self.promise = promise self.enableShutterSound = enableShutterSound super.init() delegatesReferences.append(self) } func photoOutput(_: AVCapturePhotoOutput, willCapturePhotoFor _: AVCaptureResolvedPhotoSettings) { if !enableShutterSound { // disable system shutter sound (see https://stackoverflow.com/a/55235949/5281431) AudioServicesDisposeSystemSoundID(1108) } } func photoOutput(_: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { defer { delegatesReferences.removeAll(where: { $0 == self }) } if let error = error as NSError? { promise.reject(error: .capture(.unknown(message: error.description)), cause: error) return } let error = ErrorPointer(nilLiteral: ()) guard let tempFilePath = RCTTempFilePath("jpeg", error) else { promise.reject(error: .capture(.createTempFileError), cause: error?.pointee) return } let url = URL(string: "file://\(tempFilePath)")! guard let data = photo.fileDataRepresentation() else { promise.reject(error: .capture(.fileError)) return } do { try data.write(to: url) let exif = photo.metadata["{Exif}"] as? [String: Any] let width = exif?["PixelXDimension"] let height = exif?["PixelYDimension"] let exifOrientation = photo.metadata[String(kCGImagePropertyOrientation)] as? UInt32 ?? CGImagePropertyOrientation.up.rawValue let cgOrientation = CGImagePropertyOrientation(rawValue: exifOrientation) ?? CGImagePropertyOrientation.up let orientation = getOrientation(forExifOrientation: cgOrientation) let isMirrored = getIsMirrored(forExifOrientation: cgOrientation) promise.resolve([ "path": tempFilePath, "width": width as Any, "height": height as Any, "orientation": orientation, "isMirrored": isMirrored, "isRawPhoto": photo.isRawPhoto, "metadata": photo.metadata, "thumbnail": photo.embeddedThumbnailPhotoFormat as Any, ]) } catch { promise.reject(error: .capture(.fileError), cause: error as NSError) } } func photoOutput(_: AVCapturePhotoOutput, didFinishCaptureFor _: AVCaptureResolvedPhotoSettings, error: Error?) { defer { delegatesReferences.removeAll(where: { $0 == self }) } if let error = error as NSError? { promise.reject(error: .capture(.unknown(message: error.description)), cause: error) return } } private func getOrientation(forExifOrientation exifOrientation: CGImagePropertyOrientation) -> String { switch exifOrientation { case .up, .upMirrored: return "portrait" case .down, .downMirrored: return "portrait-upside-down" case .left, .leftMirrored: return "landscape-left" case .right, .rightMirrored: return "landscape-right" default: return "portrait" } } private func getIsMirrored(forExifOrientation exifOrientation: CGImagePropertyOrientation) -> Bool { switch exifOrientation { case .upMirrored, .rightMirrored, .downMirrored, .leftMirrored: return true default: return false } } }