Skip to content

Commit eec63a8

Browse files
authored
Merge pull request #185 from jpalten/swifty-result-handling
Change callbacks to the swifty way of reporting success and errors.
2 parents 240af8b + 8770d7b commit eec63a8

3 files changed

Lines changed: 163 additions & 22 deletions

File tree

camera.xcodeproj/project.pbxproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@
206206
TargetAttributes = {
207207
454C1F4019E82E2500C81915 = {
208208
CreatedOnToolsVersion = 6.0.1;
209-
DevelopmentTeam = QM7HJTY23M;
210209
LastSwiftMigration = 1000;
211210
ProvisioningStyle = Automatic;
212211
};

camera/CameraManager.swift

Lines changed: 157 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,87 @@ public enum CameraOutputQuality: Int {
3636
case low, medium, high
3737
}
3838

39+
public enum CaptureResult {
40+
case success(content: CaptureContent)
41+
case failure(Error)
42+
43+
init(_ image: UIImage) {
44+
self = .success(content: .image(image))
45+
}
46+
47+
init(_ data: Data) {
48+
self = .success(content: .imageData(data))
49+
}
50+
51+
init(_ asset: PHAsset) {
52+
self = .success(content: .asset(asset))
53+
}
54+
55+
var imageData: Data? {
56+
if case let .success(content) = self {
57+
return content.asData
58+
} else {
59+
60+
return nil
61+
}
62+
}
63+
}
64+
65+
public enum CaptureContent {
66+
case imageData(Data)
67+
case image(UIImage)
68+
case asset(PHAsset)
69+
}
70+
71+
extension CaptureContent {
72+
73+
public var asImage: UIImage? {
74+
75+
switch self {
76+
case let .image(image): return image
77+
case let .imageData(data): return UIImage(data: data)
78+
case let .asset(asset):
79+
if let data = getImageData(fromAsset: asset) {
80+
return UIImage(data: data)
81+
} else {
82+
return nil
83+
}
84+
}
85+
}
86+
87+
public var asData: Data? {
88+
89+
switch self {
90+
case let .image(image): return image.jpegData(compressionQuality: 0.8)
91+
case let .imageData(data): return data
92+
case let .asset(asset): return getImageData(fromAsset: asset)
93+
}
94+
95+
}
96+
97+
private func getImageData(fromAsset asset: PHAsset) -> Data? {
98+
99+
var imageData: Data? = nil
100+
let manager = PHImageManager.default()
101+
let options = PHImageRequestOptions()
102+
options.version = .original
103+
options.isSynchronous = true
104+
manager.requestImageData(for: asset, options: options) { data, _, _, _ in
105+
106+
imageData = data
107+
}
108+
return imageData
109+
}
110+
}
111+
112+
public enum CaptureError: Error {
113+
114+
case noImageData
115+
case invalidImageData
116+
case noVideoConnection
117+
case noSampleBuffer
118+
case assetNotSaved
119+
}
39120

40121
/// Class for handling iDevices custom camera usage
41122
open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGestureRecognizerDelegate {
@@ -407,16 +488,45 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest
407488
self.movieOutput = nil
408489
self.animateCameraDeviceChange = oldAnimationValue
409490
}
410-
491+
411492
/**
412493
Captures still image from currently running capture session.
413-
494+
414495
:param: imageCompletion Completion block containing the captured UIImage
415496
*/
497+
@available(*, deprecated)
416498
open func capturePictureWithCompletion(_ imageCompletion: @escaping (UIImage?, NSError?) -> Void) {
417-
self.capturePictureDataWithCompletion { data, error in
418-
guard error == nil, let imageData = data else {
419-
imageCompletion(nil, error)
499+
500+
func completion(_ result: CaptureResult) {
501+
502+
switch result {
503+
504+
case let .success(content):
505+
imageCompletion(content.asImage, nil)
506+
case .failure:
507+
imageCompletion(nil, NSError())
508+
}
509+
}
510+
511+
capturePictureWithCompletion(completion)
512+
}
513+
514+
/**
515+
Captures still image from currently running capture session.
516+
517+
:param: imageCompletion Completion block containing the captured UIImage
518+
*/
519+
open func capturePictureWithCompletion(_ imageCompletion: @escaping (CaptureResult) -> Void) {
520+
self.capturePictureDataWithCompletion { result in
521+
522+
guard let imageData = result.imageData else {
523+
524+
if case let .failure(error) = result {
525+
imageCompletion(.failure(error))
526+
} else {
527+
imageCompletion(.failure(CaptureError.noImageData))
528+
}
529+
420530
return
421531
}
422532

@@ -430,9 +540,9 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest
430540
}
431541
}
432542

433-
fileprivate func _capturePicture(_ imageData: Data, _ imageCompletion: @escaping (UIImage?, NSError?) -> Void) {
543+
fileprivate func _capturePicture(_ imageData: Data, _ imageCompletion: @escaping (CaptureResult) -> Void) {
434544
guard let img = UIImage(data: imageData) else {
435-
imageCompletion(nil, NSError())
545+
imageCompletion(.failure(NSError()))
436546
return
437547
}
438548

@@ -459,12 +569,12 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest
459569
}
460570

461571
} catch {
462-
imageCompletion(nil, NSError())
572+
imageCompletion(.failure(error))
463573
return
464574
}
465575
}
466576

467-
imageCompletion(image, nil)
577+
imageCompletion(CaptureResult(image))
468578
}
469579

470580
fileprivate func _setVideoWithGPS(forLocation location: CLLocation) {
@@ -525,20 +635,48 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest
525635
return GPSMetadata
526636
}
527637

528-
fileprivate func _saveImageToLibrary(atFileURL filePath: URL, _ imageCompletion: @escaping (UIImage?, NSError?) -> Void) {
638+
fileprivate func _saveImageToLibrary(atFileURL filePath: URL, _ imageCompletion: @escaping (CaptureResult) -> Void) {
529639

530640
let location = self.locationManager?.latestLocation
531641
let date = Date()
532642

533-
library!.save(imageAtURL: filePath, albumName: self.imageAlbumName, date: date, location: location)
643+
library?.save(imageAtURL: filePath, albumName: self.imageAlbumName, date: date, location: location) { asset in
644+
645+
if let asset = asset {
646+
imageCompletion(CaptureResult(asset))
647+
} else {
648+
imageCompletion(.failure(CaptureError.assetNotSaved))
649+
}
650+
}
534651
}
535652

536653
/**
537654
Captures still image from currently running capture session.
538655

539656
:param: imageCompletion Completion block containing the captured imageData
540657
*/
658+
@available(*, deprecated)
541659
open func capturePictureDataWithCompletion(_ imageCompletion: @escaping (Data?, NSError?) -> Void) {
660+
661+
func completion(_ result: CaptureResult) {
662+
663+
switch result {
664+
665+
case let .success(content):
666+
imageCompletion(content.asData, nil)
667+
case .failure:
668+
imageCompletion(nil, NSError())
669+
}
670+
}
671+
capturePictureDataWithCompletion(completion)
672+
}
673+
674+
/**
675+
Captures still image from currently running capture session.
676+
677+
:param: imageCompletion Completion block containing the captured imageData
678+
*/
679+
open func capturePictureDataWithCompletion(_ imageCompletion: @escaping (CaptureResult) -> Void) {
542680
guard cameraIsSetup else {
543681
_show(NSLocalizedString("No capture session setup", comment:""), message: NSLocalizedString("I can't take any picture", comment:""))
544682
return
@@ -569,17 +707,20 @@ open class CameraManager: NSObject, AVCaptureFileOutputRecordingDelegate, UIGest
569707
DispatchQueue.main.async(execute: {
570708
self?._show(NSLocalizedString("Error", comment:""), message: error.localizedDescription)
571709
})
572-
imageCompletion(nil, error as NSError?)
710+
imageCompletion(.failure(error))
573711
return
574712
}
575713

576-
guard let sample = sample else { imageCompletion(nil, NSError()); return }
577-
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sample)
578-
imageCompletion(imageData, nil)
714+
guard let sample = sample else { imageCompletion(.failure(CaptureError.noSampleBuffer)); return }
715+
if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sample) {
716+
imageCompletion(CaptureResult(imageData))
717+
} else {
718+
imageCompletion(.failure(CaptureError.noImageData))
719+
}
579720

580721
})
581722
} else {
582-
imageCompletion(nil, NSError())
723+
imageCompletion(.failure(CaptureError.noVideoConnection))
583724
}
584725
})
585726
}

camera/ViewController.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,15 @@ class ViewController: UIViewController {
138138

139139
switch cameraManager.cameraOutputMode {
140140
case .stillImage:
141-
cameraManager.capturePictureWithCompletion({ (image, error) -> Void in
142-
if error != nil {
141+
cameraManager.capturePictureWithCompletion({ result in
142+
switch result {
143+
case .failure:
143144
self.cameraManager.showErrorBlock("Error occurred", "Cannot save picture.")
144-
}
145-
else {
145+
case .success(let content):
146+
146147
let vc: ImageViewController? = self.storyboard?.instantiateViewController(withIdentifier: "ImageVC") as? ImageViewController
147148
if let validVC: ImageViewController = vc,
148-
let capturedImage = image {
149+
case let capturedImage = content.asImage {
149150
validVC.image = capturedImage
150151
validVC.cameraManager = self.cameraManager
151152
self.navigationController?.pushViewController(validVC, animated: true)

0 commit comments

Comments
 (0)