title: How can I capture photo and video from the same AVCaptureSession?
body: <p>I'm trying to make a SnapChat-like app. From the same button, I'm willing to allow user to take pictures (touch up inside) and record video (long press).</p>
  
  <p>I'm using AVFoundation for it. The tricky part is that I can't make it work properly in the same AVCaptureSession. I mean, I have only 1 preview layer for both captures, how can I start the right one depending on user's interactions with record button? Did someone already worked with something similar?</p>
  
  <p>Here's a piece of my code:</p>
  
  <pre><code>import UIKit
  import AVFoundation
  
  protocol RecordCameraDelegate {
      func didSavedOutputFile(url: URL!, error: Error?)
      func didSavedImage(image: UIImage?)
  }
  
  // MARK: - Camera
  class RecordCamera : NSObject {
  
      var videoLayer : AVCaptureVideoPreviewLayer!
      var delegate : RecordCameraDelegate!
      var capturedPhoto : UIImage?
  
      fileprivate var captureSession = AVCaptureSession()
      fileprivate var photoSession = AVCaptureSession()
  
      fileprivate var movieOutput = AVCaptureMovieFileOutput()
      fileprivate var cameraDevice : AVCaptureDevicePosition!
      fileprivate let stillImageOutput = AVCaptureStillImageOutput()
  
      // Devices
      fileprivate lazy var frontCameraDevice: AVCaptureDevice? = {
          let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as! [AVCaptureDevice]
          return devices.filter { $0.position == .front }.first
      }()
  
      fileprivate lazy var backCameraDevice: AVCaptureDevice? = {
          let devices = AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) as! [AVCaptureDevice]
          return devices.filter { $0.position == .back }.first
      }()
  
      fileprivate lazy var micDevice: AVCaptureDevice? = {
          return AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
      }()
  
      fileprivate var tempFilePath: URL = {
          let tempPath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("bighug").appendingPathExtension("mp4").absoluteString
          if FileManager.default.fileExists(atPath: tempPath) {
              do {
                  try FileManager.default.removeItem(atPath: tempPath)
              } catch let error { print("Can't create File URL: \(String(describing: error))") }
          }
          return URL(string: tempPath)!
      }()
  
      // MARK: - Initialization
      init(view: UIView, cameraPosition: AVCaptureDevicePosition = .front) {
          super.init()
  
          cameraDevice = cameraPosition
  
          // Video
          self.configureToRecord(view: view)
          // Photo
          self.configureToCapturePhoto()
      }
  
      func configureToRecord(view: UIView? = nil) {
  
          captureSession.beginConfiguration()
          defer {
              // commit &amp; stop session
              captureSession.commitConfiguration()
              if  !captureSession.isRunning { captureSession.startRunning() }
          }
  
          captureSession.sessionPreset = AVCaptureSessionPresetHigh
  
          // Start configuration
          if !captureSession.isRunning {
  
              // layer
              if  let validView = view {
                  videoLayer = AVCaptureVideoPreviewLayer(session: captureSession)
                  videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
                  videoLayer.frame = validView.bounds
                  validView.layer.addSublayer(videoLayer)
              }
  
              // add device inputs (front camera and mic)
              if  cameraDevice == .front {
                  captureSession.addInput(deviceInputFrom(device: frontCameraDevice))
              } else {
                  captureSession.addInput(deviceInputFrom(device: backCameraDevice))
              }
          }
  
          captureSession.addInput(deviceInputFrom(device: micDevice))
  
          // Output
          movieOutput.movieFragmentInterval = kCMTimeInvalid
  
          // Remove previous output
          if  let existingOutput = captureSession.outputs.first as? AVCaptureOutput {
              captureSession.removeOutput(existingOutput)
          }
          // Add Movie Output
          if  captureSession.canAddOutput(movieOutput) {
              captureSession.addOutput(movieOutput)
          }
      }
  
      func configureToCapturePhoto() {
  
          photoSession.beginConfiguration()
          defer { photoSession.commitConfiguration() }
  
          photoSession.sessionPreset = AVCaptureSessionPresetPhoto
          stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
  
          if #available(iOS 10.0, *) {
              let cameraOutput = AVCapturePhotoOutput()
              // Add Photo Output
              if  photoSession.canAddOutput(cameraOutput) {
                  photoSession.addOutput(cameraOutput)
              }
          }
          else {
              // Add Photo Output
              if  photoSession.canAddOutput(stillImageOutput) {
                  photoSession.addOutput(stillImageOutput)
              }
          }
      }
  
      func takePicture() {
          if #available(iOS 10.0, *) {
              let cameraOutput = photoSession.outputs.first as! AVCapturePhotoOutput
              // Capture Picture
              let settings = AVCapturePhotoSettings()
              let previewPixelType = settings.availablePreviewPhotoPixelFormatTypes.first!
              let previewFormat = [
                  kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
                  kCVPixelBufferWidthKey as String: 828,
                  kCVPixelBufferHeightKey as String: 828
              ]
              settings.previewPhotoFormat = previewFormat
              cameraOutput.capturePhoto(with: settings, delegate: self)
          }
          else {
              if  let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
                  stillImageOutput.captureStillImageAsynchronously(from: videoConnection) { (imageDataSampleBuffer, error) -&gt; Void in
                      let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer)
                      //UIImageWriteToSavedPhotosAlbum(UIImage(data: imageData!)!, nil, nil, nil)
                      guard let validData = imageData else { self.delegate?.didSavedImage(image: nil); return }
                      self.capturedPhoto = UIImage(data: validData)
                  }
              }
          }
      }
  
      // MARK: - Record Methods
      func startRecording() {
          // Take picture
          print("Camera started recording")
          self.takePicture()
          // Start recording
          movieOutput.startRecording(
              toOutputFileURL: tempFilePath,
              recordingDelegate: self
          )
      }
  
      func stopRecording() {
          print("Camera stopped recording")
          movieOutput.stopRecording()
      }
  
      // MARK: - Modes
      func cameraMode() {
  
          captureSession.beginConfiguration()
          defer { captureSession.commitConfiguration() }
  
          let inputs: [AVCaptureInput] = captureSession.inputs?.flatMap { $0 as? AVCaptureInput } ?? []
  
          // From
          if  cameraDevice == .front {
              if  let validFrontDevice = deviceInputFrom(device: frontCameraDevice) {
                  if !inputs.contains(validFrontDevice) {
                      captureSession.addInput(validFrontDevice)
                  }
              }
          }
          // Back
          if  cameraDevice == .back {
              if  let validBackDevice = deviceInputFrom(device: backCameraDevice) {
                  if !inputs.contains(validBackDevice) {
                      captureSession.addInput(validBackDevice)
                  }
              }
          }
  
          print("Record Camera --&gt; Set VIDEO Mode")
      }
  
      func audioMode() {
  
          captureSession.beginConfiguration()
          defer { captureSession.commitConfiguration() }
  
          let inputs: [AVCaptureInput] = captureSession.inputs?.flatMap { $0 as? AVCaptureInput } ?? []
  
          // Remove..
          for input in inputs {
              if  let deviceInput = input as? AVCaptureDeviceInput {
                  if  deviceInput.device == backCameraDevice
                  ||  deviceInput.device == frontCameraDevice {
                      captureSession.removeInput(deviceInput)
                  }
              }
          }
  
          print("Record Camera --&gt; Set AUDIO Mode")
      }
  
      // MARK: - Util methods
      fileprivate func deviceInputFrom(device: AVCaptureDevice?) -&gt; AVCaptureDeviceInput? {
          guard let validDevice = device else { return nil }
          do {
              return try AVCaptureDeviceInput(device: validDevice)
          } catch let outError {
              print("Device setup error occured: \(String(describing: outError))")
              return nil
          }
      }
  
      func swipeCamera() {
  
          cameraDevice = cameraDevice == .front ? .back : .front
  
          captureSession.beginConfiguration()
          defer { captureSession.commitConfiguration() }
  
          let inputs: [AVCaptureInput] = captureSession.inputs?.flatMap { $0 as? AVCaptureInput } ?? []
  
          // Remove...
          for input in inputs {
              if  let deviceInput = input as? AVCaptureDeviceInput {
                  if  deviceInput.device == backCameraDevice &amp;&amp; cameraDevice == .front {
                      captureSession.removeInput(deviceInput)
                      photoSession.removeInput(deviceInput)
                      break;
                  } else if deviceInput.device == frontCameraDevice &amp;&amp; cameraDevice == .back {
                      captureSession.removeInput(deviceInput)
                      photoSession.removeInput(deviceInput)
                      break;
                  }
              }
          }
  
          // From
          if  cameraDevice == .front {
              if  let validFrontDevice = deviceInputFrom(device: frontCameraDevice) {
                  if !inputs.contains(validFrontDevice) {
                      captureSession.addInput(validFrontDevice)
                      photoSession.addInput(validFrontDevice)
                      print("Record Camera --&gt; Swipe to Front Camera")
                  }
              }
          }
          // Back
          if  cameraDevice == .back {
              if  let validBackDevice = deviceInputFrom(device: backCameraDevice) {
                  if !inputs.contains(validBackDevice) {
                      captureSession.addInput(validBackDevice)
                      photoSession.addInput(validBackDevice)
                      print("Record Camera --&gt; Swipe to Back Camera")
                  }
              }
          }
      }
  }
  
  // MARK: - Capture Output
  extension RecordCamera : AVCaptureFileOutputRecordingDelegate {
  
      func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
          // Not implemented
      }
  
      func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
          guard error == nil else {
              if  let photo = capturedPhoto {
                  delegate?.didSavedImage(image: photo)
              }
              return
          }
          delegate?.didSavedOutputFile(url: outputFileURL, error: error)
      }
  }
  
  @available(iOS 10.0, *)
  extension RecordCamera : AVCapturePhotoCaptureDelegate {
  
      func capture(_ captureOutput: AVCapturePhotoOutput, didCapturePhotoForResolvedSettings resolvedSettings: AVCaptureResolvedPhotoSettings) {
          print("Picture taken")
      }
  
      func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) {
  
          guard error == nil else {
              print("Failed Capturing Picture: \(String(describing: error!.localizedDescription))")
              capturedPhoto = nil
              //self.delegate.didSavedImage(image: nil)
              return
          }
  
          if  let sampleBuffer = photoSampleBuffer, let previewBuffer = previewPhotoSampleBuffer,
              let imageData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: sampleBuffer, previewPhotoSampleBuffer: previewBuffer) {
              print("Photo Saved!")
              capturedPhoto = UIImage(data: imageData)
              //self.delegate.didSavedImage(image: image)
          }
  
      }
  }
  </code></pre>