swiftswiftuiphotospicker

How to get original video creation date with PhotosPicker


I'm trying to get the original video file creation date when importing a video using PhotosPicker, and am unable to get anything other than the newly copied file's creation date which will always be the moment the file was imported.

I have tried various ways using the PhotosPicker but haven't had any success in getting the actual date. I have also attempted to get the original video creation date with PHPhotoPicker and UIImagePicker and have had the same result. It should be extremely easy to get but I can't find an example anywhere.

Here is a minimal example:

import SwiftUI
import PhotosUI

struct ContentView: View {
  
  @State var showPicker = false
  @State var selectedItem: PhotosPickerItem?
  
  var body: some View {
    VStack {
      Button("PHPicker") {
        showPicker.toggle()
      }
      .photosPicker(isPresented: $showPicker, selection: $selectedItem, matching: .videos)
      .padding()
      .onChange(of: selectedItem) { newValue in
        if let newValue {
          Task {
            do {
              let pickedMovie = try await newValue.loadTransferable(type: VideoPickerTransferable.self)
            }
          }
        }
      }
    }
  }
}

#Preview {
    ContentView()
}

struct VideoPickerTransferable: Transferable {
  let videoUrl: URL
  static var transferRepresentation: some TransferRepresentation {
    FileRepresentation(contentType: .movie) { exportingFile in
      return .init(exportingFile.videoUrl)
      
    } importing: { receivedTransferredFile in
      let originalFile = receivedTransferredFile.file
      print(try FileManager.default.attributesOfItem(atPath: originalFile.path())[FileAttributeKey.creationDate])
      let copiedFile = URL.documentsDirectory.appending(path: "videoPicker.mov")
      
      if FileManager.default.fileExists(atPath: copiedFile.path()) {
        try FileManager.default.removeItem(at: copiedFile)
      }
      
      try FileManager.default.copyItem(at: originalFile, to: copiedFile)
      
      return .init(videoUrl: copiedFile)
    }
  }
}

Solution

  • You can use the rendition of photosPicker that takes a photoLibrary parameter. When you do that, the PhotosPickerItem will include a itemIdentifier that you can use with PHAsset.fetchAssets:

    struct ContentView: View {
        @State var showPicker = false
        @State var selectedItem: PhotosPickerItem?
    
        var body: some View {
            VStack {
                Button("Photo picker") {
                    showPicker.toggle()
                }
                .photosPicker(
                    isPresented: $showPicker,
                    selection: $selectedItem,
                    matching: .videos,
                    photoLibrary: .shared()
                )
                .padding()
                .onChange(of: selectedItem) {
                    guard
                        let identifier = selectedItem?.itemIdentifier,
                        let asset = PHAsset.fetchAssets(
                            withLocalIdentifiers: [identifier],
                            options: nil
                        ).firstObject,
                        let creationDate = asset.creationDate
                    else {
                        print("unable to fetch date")
                        return
                    }
    
                    print(creationDate)
                }
            }
        }
    }
    

    By the way, you will need to add a “Privacy - Photo Library Usage Description”, i.e., NSPhotoLibraryUsageDescription, to your Info.plist.