Skip to content

Instantly share code, notes, and snippets.

@Rukh
Created May 12, 2023 12:35
Show Gist options
  • Save Rukh/7cc0889b737bccd5388228df0fe6c433 to your computer and use it in GitHub Desktop.
Save Rukh/7cc0889b737bccd5388228df0fe6c433 to your computer and use it in GitHub Desktop.
Async / await capture photo
import AVKit
extension AVCapturePhotoOutput {
enum Failure: Error {
case capturePhotoFailed
}
private final class CaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
typealias OnTakePhoto = (CIImage?) -> Void
let isFrontCamera: Bool
var onTakePhoto: OnTakePhoto?
init(isFrontCamera: Bool, onTakePhoto: OnTakePhoto? = nil) {
self.isFrontCamera = isFrontCamera
self.onTakePhoto = onTakePhoto
}
public func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
// Hack, using for dispose system shutter sound
AudioServicesDisposeSystemSoundID(1108)
}
public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
var image: CIImage?
if let data = photo.fileDataRepresentation() {
image = CIImage( // best way for convert AVCapturePhoto to CIImage without loose metadata
data: data,
options: [
.properties : photo.metadata,
.applyOrientationProperty : true,
]
)
}
if isFrontCamera {
image = image?.oriented(.upMirrored)
}
onTakePhoto?(image)
}
}
func capturePhoto(with settings: AVCapturePhotoSettings, isFrontCamera: Bool = false) async throws -> CIImage {
try await withCheckedThrowingContinuation { continuation in
guard settings as? AVCapturePhotoBracketSettings == nil else {
continuation.resume(throwing: Failure.capturePhotoFailed)
return assertionFailure()
}
let delegate = CaptureDelegate(isFrontCamera: isFrontCamera)
delegate.onTakePhoto = { [delegate] in // looped delegate reference for avoid auto release
if let image = $0 {
continuation.resume(returning: image)
} else {
continuation.resume(throwing: Failure.capturePhotoFailed)
}
delegate.onTakePhoto = nil // release
}
self.capturePhoto(with: settings, delegate: delegate)
}
}
func capturePhotoBracket(with settings: AVCapturePhotoSettings, isFrontCamera: Bool) -> AsyncStream<CIImage> {
AsyncStream(bufferingPolicy: .unbounded) { continuation in
guard let settings = settings as? AVCapturePhotoBracketSettings else {
continuation.finish()
return assertionFailure()
}
var takedPhotos = 0
let delegate = CaptureDelegate(isFrontCamera: isFrontCamera)
delegate.onTakePhoto = { [delegate] photo in // looped delegate reference for avoid auto release
takedPhotos += 1
if let photo {
continuation.yield(photo)
}
if takedPhotos == settings.bracketedSettings.count {
continuation.finish()
delegate.onTakePhoto = nil // release
}
}
self.capturePhoto(with: settings, delegate: delegate)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment