Article

Capturing and Saving Live Photos

Capture Live Photos like those created in the system Camera app and save them to the Photos library.

Overview

A Live Photo is a picture that includes motion and sound from the moments just before and after its capture. Your app can easily capture and record Live Photos using the AVFoundation capture system and the AVCapturePhotoOutput class.

Enable Live Photo Capture

For a still photo your capture session needs only a video input, but a Live Photo includes sound, so you'll need to also connect an audio capture device to your session:

self.captureSession.beginConfiguration()
// ... add camera input and photo output ...

guard
    let audioDevice = AVCaptureDevice.default(for: .audio),
    let audioDeviceInput = try? AVCaptureDeviceInput(device: audioDevice),
    self.captureSession.canAddInput(audioDeviceInput)
    else { return }
self.captureSession.addInput(audioDeviceInput)

self.captureSession.commitConfiguration()
// ... configure photo output and start running ...

Because you're already using a built-in camera device for video (see Setting Up a Capture Session), you can simply use the default audio capture device—the system automatically uses the best microphone configuration for the camera position.

Capturing Live Photos requires an internal reconfiguration of the capture pipeline, which takes time and interrupts any in-progress captures. Before shooting your first Live Photo, make sure the pipeline is configured appropriately by enabling Live Photo capture on your AVCapturePhotoOutput object:

let photoOutput = AVCapturePhotoOutput()
photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported

guard self.captureSession.canAddOutput(photoOutput) else { return }
self.captureSession.sessionPreset = .photo
self.captureSession.addOutput(photoOutput)

self.previewView.session = captureSession
captureSession.startRunning()

Capture a Live Photo

Once your photo output is ready for Live Photos, you can choose still image or Live Photo capture for each shot. To capture a Live Photo, create an AVCapturePhotoSettings object, choosing the format for the still image portion of the Live Photo and providing a URL for writing the movie portion of the Live Photo. Then, call capturePhoto(with:delegate:) to trigger capture:

let photoSettings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])
photoSettings.livePhotoMovieFileURL = self.makeUniqueTempFileURL(typeExtension: "mov")

// Shoot the Live Photo, using a custom class to handle capture delegate callbacks.
let captureProcessor = LivePhotoCaptureProcessor()
self.photoOutput.capturePhoto(with: photoSettings, delegate: captureProcessor)

Handle Live Photo Results

A Live Photo appears to users in the Photos app as a single asset, but it's actually composed of separate files: the primary still image, and a movie file containing motion and sound from the moments before and after. The capture system delivers these results separately, as soon as each becomes available.

The photoOutput(_:didFinishProcessingPhoto:error:) method delivers the still image portion of the Live Photo as an AVCapturePhoto object. Because you'll need to save the still image and movie files together, it's best to extract the image file data from the AVCapturePhoto and keep it until the movie file finishes recording, as shown below. (You can also use this method to indicate in your UI that the still image has been captured.)

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {    guard error != nil else { print("Error capturing Live Photo still: \(error!)"); return }

    self.stillImageData = photo.fileDataRepresentation()
}

The photoOutput(_:didFinishProcessingLivePhotoToMovieFileAt:duration:photoDisplayTime:resolvedSettings:error:) method fires later, indicating that the URL you specified when triggering the capture now contains a complete movie file. Once you have both the still image and movie portions of your Live Photo, you can save them together:

func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, duration: CMTime, photoDisplayTime: CMTime, resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
    guard error != nil else { print("Error capturing Live Photo movie: \(error!)"); return }
    guard let stillImageData = self.stillImageData else { return }
    
    self.saveLivePhotoToPhotosLibrary(stillImageData: stillImageData,
                                      livePhotoMovieURL: outputFileURL)
}

Save a Live Photo to the Photos Library

Use the PHAssetCreationRequest class to create a single Photos asset consisting of media from multiple files—in the case of a Live Photo, the still image and its paired video. As in Saving Captured Photos, you'll need to wrap that request in a PHPhotoLibrary change block, and first make sure that your app has the user's permission to access Photos.

func saveLivePhotoToPhotosLibrary(stillImageData: Data, livePhotoMovieURL: URL) {
    PHPhotoLibrary.requestAuthorization { status in
        guard status == .authorized else { return }
        
        PHPhotoLibrary.shared().performChanges({
            // Add the captured photo's file data as the main resource for the Photos asset.
            let creationRequest = PHAssetCreationRequest.forAsset()
            creationRequest.addResource(with: .photo, data: stillImageData, options: nil)
        
            // Add the movie file URL as the Live Photo's paired video resource.
            let options = PHAssetResourceCreationOptions()
            options.shouldMoveFile = true
            creationRequest.addResource(with: .pairedVideo, fileURL: livePhotoMovieURL, options: options)
        }, completionHandler: self.handlePhotoLibraryError)
    }
}

Track Live Photo Progress

Capturing Live Photos adds two additional steps to the process shown in Tracking Photo Capture Progress: after delivery of the still photo result (step 4), the photo output notifies you of movie capture status (step 5) and delivers the movie result (step 6). (Final cleanup becomes step 7.)

Diagram of numbered Live Photo capture process steps after calling capturePhoto().

When the user captures a Live Photo in the system Camera app, a "Live" indicator appears for a few seconds to let the user know that video and audio are still being recorded. To provide a similar interface in your app, implement these methods in your photo capture delegate:

You can have multiple Live Photo captures running at the same time, so it's best to use these methods to keep track of the number of captures "in flight" and hide your indicator only when that number reaches zero:

class LivePhotoCaptureProcessor: NSObject, AVCapturePhotoCaptureDelegate {
    // ... other PhotoCaptureDelegate methods and supporting properties ...

    // A handler to call when Live Photo capture begins and ends.
    var livePhotoStatusHandler: (Bool) -> () = { _ in }

    // Call the handler when PhotoCaptureDelegate methods indicate Live Photo capture is in progress.
    func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
        let capturingLivePhoto = (resolvedSettings.livePhotoMovieDimensions.width > 0 && resolvedSettings.livePhotoMovieDimensions.height > 0)
        livePhotoStatusHandler(capturingLivePhoto)
    }
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishRecordingLivePhotoMovieForEventualFileAt outputFileURL: URL, resolvedSettings: AVCaptureResolvedPhotoSettings) {
        livePhotoStatusHandler(false)
    }
}

// A property for tracking in-progress captures and updating UI accordingly.
var livePhotosInProgress = 0 {
    didSet {
        self.capturingLivePhotoLabel.isHidden = (livePhotosInProgress > 0)
    }
}

func shootLivePhoto() {
    // Create instance of delegate class to handle this Live Photo, and
    // schedule its handler to update the count of Live Photos in progress.
    let captureProcessor = LivePhotoCaptureProcessor()
    captureProcessor.livePhotoStatusHandler = { isCapturing in
        self.livePhotosInProgress += (isCapturing ? +1 : -1)
    }
    self.photoOutput.capturePhoto(with: self.settingsForLivePhoto(), delegate: captureProcessor)
}

See Also

Next Steps

Saving Captured Photos

Add the image and other data from a photo capture to the Photos library.

Tracking Photo Capture Progress

Monitor key events during capture to provide feedback in your camera UI.