title: Swift: Using AVFoundation to Capture Stills
body: <p>I've been attempting to tackle this subject for a class, but it has been an absolute nightmare for me. My project is simple note-taking application. It has a table of notes and when you click the button to add a note, it modally presents a new view controller where you can enter note content. In this view there is a camera button, which initiates a segue to the view controller posted below. <strong>The goal here is to allow still images to be added to these "note" objects (which contain text and the image).</strong> I hope you can tolerate this code, which I'm sure is extremely messy and amateurish. </p>
  
  <p>Here is the full code for the custom view being used by the camera view controller:</p>
  
  <pre><code>import UIKit
  import AVFoundation
  
      class DEZCamPreviewView: UIView {
  
      var session: AVCaptureSession! {
          get {
              return (self.layer as AVCaptureVideoPreviewLayer).session
          }
  
          set {
              (self.layer as AVCaptureVideoPreviewLayer).session = newValue
          }
      }
  
      override class func layerClass() -&gt; AnyClass {
          return AVCaptureVideoPreviewLayer.self
      }
  
  }
  </code></pre>
  
  <p>Here is the full code for the camera view controller:</p>
  
  <pre><code>import UIKit
  import AVFoundation
  import AssetsLibrary
  
  class DEZCamViewController: UIViewController {
  
  // Store the note to send back it's image.
  var note: Note?
  
  @IBOutlet weak var captureButton: UIButton!
  @IBOutlet weak var previewView: DEZCamPreviewView!
  // Session management.
  var sessionQueue: dispatch_queue_t! // Communicate with the session and other session objects on this queue.
  var session: AVCaptureSession!
  var videoDeviceInput: AVCaptureDeviceInput!
  var stillImageOutput: AVCaptureStillImageOutput!
  
  override func viewDidLoad() {
      super.viewDidLoad()
  
      self.session = AVCaptureSession()
  
      // Setup the preview view
      self.previewView.session = session
  
      self.sessionQueue = dispatch_queue_create("session queue", DISPATCH_QUEUE_SERIAL)
  
      dispatch_async(sessionQueue) {
          //self.backgroundRecordingID = UIBackgroundTaskInvalid
  
          var error: NSError?
  
          let videoDevice: AVCaptureDevice = DEZCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .Back)
  
          let videoDeviceInput: AVCaptureDeviceInput = AVCaptureDeviceInput(device: videoDevice, error: &amp;error)
  
          if let error = error {
              println(error.localizedDescription)
          }
  
          if self.session.canAddInput(videoDeviceInput) {
              self.session.addInput(videoDeviceInput)
              self.videoDeviceInput = videoDeviceInput
  
              dispatch_async(dispatch_get_main_queue()) {
                  // Why are we dispatching this to the main queue?
                  // Because AVCaptureVideoPreviewLayer is the backing layer for AVCamPreviewView and 
                  // UIView can only be manipulated on main thread.
                  // Note: As an exception to the above rule, it is not necessary to serialize video 
                  // orientation changes on the AVCaptureVideoPreviewLayer’s connection with other 
                  // session manipulation.
                  let avLayer = (self.previewView.layer as AVCaptureVideoPreviewLayer)
                  let connection = avLayer.connection
                  let orientation = AVCaptureVideoOrientation(rawValue: self.interfaceOrientation.rawValue)!
                  connection.videoOrientation = orientation
              }
          }
  
          if let error = error {
              println(error.localizedDescription)
          }
  
          let stillImageOutput = AVCaptureStillImageOutput()
          if self.session.canAddOutput(stillImageOutput) {
              stillImageOutput.outputSettings[AVVideoCodecKey] = AVVideoCodecJPEG
              self.session.addOutput(stillImageOutput)
              self.stillImageOutput = stillImageOutput
          }
      }
  }
  
  override func viewWillAppear(animated: Bool) {
      dispatch_async(self.sessionQueue) {
          self.session.startRunning()
          // NEW CODE HERE
          self.captureButton.enabled = true
      }
  }
  
  override func viewWillDisappear(animated: Bool) {
      dispatch_async(self.sessionQueue) {
          self.session.stopRunning()
          println("CAM-CONTROLLER: inside viewWillDisappear -- session stop called")
      }
  }
  
  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
  }
  
  
  @IBAction func captureImageNow(sender: UIButton) {
      dispatch_async(self.sessionQueue) {
          println("CAM-CONTROLLER: inside captureImageNow")
  
         // Capture a still image.
          self.stillImageOutput.captureStillImageAsynchronouslyFromConnection(self.stillImageOutput.connectionWithMediaType(AVMediaTypeVideo))
              { (imageDataSampleBuffer, error) -&gt; Void in
  
              if let imageDataSampleBuffer = imageDataSampleBuffer {
                  println("inside closure")
                  let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
                  println("got image data:")
                  if let image = UIImage(data: imageData) {
                      println("CAM-CONTROLLER: inside captureImageNow -- set the image: \(image)")
                      // Save the image
                      self.note?.noteImage = image
  
                      self.navigationController?.popViewControllerAnimated(true)
  
                  }
              }
          }
      }
  
  }
      class func deviceWithMediaType(mediaType: NSString, preferringPosition position: AVCaptureDevicePosition) -&gt; AVCaptureDevice {
      let devices = AVCaptureDevice.devicesWithMediaType(mediaType) as [AVCaptureDevice]
      var captureDevice = devices[0]
  
      for device in devices {
          if device.position == position {
              captureDevice = device
              break
          }
      }
  
      return captureDevice;
  }
      // MARK: - Navigation
  
  
  override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
      // Get the new view controller using segue.destinationViewController.
      // Pass the selected object to the new view controller.
      if segue.identifier == "goBackToNoteSegue" {
          let controller = segue.destinationViewController as DEZAddNoteViewController
          if let strongNote = self.note {
              controller.existingNote = strongNote
          }
      }
  }
  
  
  }
  </code></pre>
  
  <p>I've run into a variety of frustrating issues. At one point, the controller was capturing images when the capture button was pressed, but instead of proceeding to the previous view it only showed the previous view for a moment before showing the camera preview...this happened 9/10 times I touched the capture button in one sitting, so I started stripping down the controller until I thought I had the bare minimum of necessary code.</p>
  
  <p>Now the problem is that the image is either never captured, or it's captured but never successfully sent back to the previous view controller. Can anyone shed light on why this is not working? Or, better yet, can anyone direct me to a <em>bare essentials</em> implementation of still image capture with AVFoundation (in Swift)?</p>