@@ -179,7 +179,7 @@ extension AttachmentCreationCoordinator: AttachmentCreationDelegate {
179179 MageLogger . misc. debug ( " Present the gallery " )
180180 var configuration = PHPickerConfiguration ( photoLibrary: PHPhotoLibrary . shared ( ) )
181181 configuration. filter = . any( of: [ . images, . videos] )
182- configuration. selectionLimit = 10 ;
182+ configuration. selectionLimit = 1 ;
183183
184184 // This is to compensate for iOS not setting all the colors on the PHPicker
185185 // it only sets the tint color not anything else, so let's make the button actually viewable
@@ -202,7 +202,8 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
202202 guard let documentsDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first else {
203203 return
204204 }
205- let uniqueId = UUID ( ) . uuidString
205+ let dateFormatter = DateFormatter ( )
206+ dateFormatter. dateFormat = " yyyyMMdd_HHmmss "
206207 let attachmentsDirectory = documentsDirectory. appendingPathComponent ( " attachments " )
207208 let requestOptions = PHImageRequestOptions ( )
208209 requestOptions. isSynchronous = true
@@ -212,7 +213,7 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
212213 guard let data else {
213214 return
214215 }
215- let scaledImagePath = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( uniqueId ) .jpeg " )
216+ let scaledImagePath = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( dateFormatter . string ( from : Date ( ) ) ) .jpeg " )
216217 do {
217218 try FileManager . default. createDirectory ( at: attachmentsDirectory, withIntermediateDirectories: true , attributes: [ . protectionKey : FileProtectionType . complete] )
218219 }
@@ -245,9 +246,10 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
245246 guard let documentsDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first else {
246247 return
247248 }
248- let uniqueId = UUID ( ) . uuidString
249+ let dateFormatter = DateFormatter ( )
250+ dateFormatter. dateFormat = " yyyyMMdd_HHmmss "
249251 let attachmentsDirectory = documentsDirectory. appendingPathComponent ( " attachments " )
250- let videoExportPath = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( uniqueId ) .mp4 " )
252+ let videoExportPath = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( dateFormatter . string ( from : Date ( ) ) ) .mp4 " )
251253 let assetRequestOptions = PHVideoRequestOptions ( )
252254 assetRequestOptions. deliveryMode = . highQualityFormat
253255 assetRequestOptions. isNetworkAccessAllowed = true
@@ -280,7 +282,7 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
280282 self . addAttachmentForSaving ( location: videoExportPath, contentType: " video/mp4 " )
281283 }
282284 }
283-
285+
284286 private func requestAVAssetAsync( forVideo: PHAsset , options: PHVideoRequestOptions ? ) async throws -> AVAsset ? {
285287 try await withCheckedThrowingContinuation { continuation in
286288 PHImageManager . default ( ) . requestAVAsset ( forVideo: forVideo, options: options) { avAsset, audioMix, info in
@@ -292,7 +294,7 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
292294 }
293295 }
294296 }
295-
297+
296298 func galleryPermissionDenied( ) {
297299 // The user selected certain photos to share with MAGE and this wasn't one of them
298300 // prompt the user to pick more photos to share
@@ -314,31 +316,47 @@ extension AttachmentCreationCoordinator: PHPickerViewControllerDelegate {
314316 }
315317
316318 func picker( _ picker: PHPickerViewController , didFinishPicking results: [ PHPickerResult ] ) {
317- MageLogger . misc. debug ( " picked photos \( results) " )
318- UINavigationBar . appearance ( ) . tintColor = self . scheme? . colorScheme. onPrimaryColor // This is to compensate for iOS not setting all the colors on the PHPicker so now we have to set it back
319-
319+ MageLogger . misc. debug ( " picked a photo \( results) " )
320+ // This is to compensate for iOS not setting all the colors on the PHPicker so now we have to set it back
321+ UINavigationBar . appearance ( ) . tintColor = self . scheme? . colorScheme. onPrimaryColor
322+
320323 guard !results. isEmpty else {
321324 picker. dismiss ( animated: true , completion: nil )
322325 return
323326 }
324327
328+ let dateFormatter = DateFormatter ( ) ;
329+ dateFormatter. dateFormat = " yyyyMMdd_HHmmss " ;
330+
325331 for result in results {
326332 let itemProvider = result. itemProvider
327- guard let assetIdentifier = result. assetIdentifier else {
328- continue // Skip invalid asset (edge case: potentially deleted during upload)
329- }
330333
331- if itemProvider. hasItemConformingToTypeIdentifier ( UTType . movie. identifier) {
332- let fetchResult = PHAsset . fetchAssets ( withLocalIdentifiers: [ assetIdentifier] , options: nil )
333- handleVideo ( selectedAsset: fetchResult. firstObject, utType: . movie)
334- } else if itemProvider. hasItemConformingToTypeIdentifier ( UTType . image. identifier) {
335- let fetchResult = PHAsset . fetchAssets ( withLocalIdentifiers: [ assetIdentifier] , options: nil )
336- handlePhoto ( selectedAsset: fetchResult. firstObject, utType: . image)
337- } else {
338- MageLogger . misc. debug ( " Could not handle asset types: \( itemProvider. registeredTypeIdentifiers) " )
334+ // find the first type that we can handle
335+ for typeIdentifier in itemProvider. registeredTypeIdentifiers {
336+ guard let utType = UTType ( typeIdentifier) else {
337+ continue
338+ }
339+ // Matches both com.apple.live-photo-bundle and com.apple.private.live-photo-bundle
340+ if utType. conforms ( to: . image) || typeIdentifier. contains ( " live-photo-bundle " ) {
341+ if let assetIdentifier = result. assetIdentifier {
342+ let options = PHFetchOptions ( )
343+ options. predicate = NSPredicate ( format: " mediaType = %d " , PHAssetMediaType . image. rawValue)
344+ let fetchResult = PHAsset . fetchAssets ( withLocalIdentifiers: [ assetIdentifier] , options: nil )
345+ handlePhoto ( selectedAsset: fetchResult. firstObject, utType: utType)
346+ picker. dismiss ( animated: true , completion: nil )
347+ return
348+ }
349+ }
350+ // otherwise it should be a movie
351+ if utType. conforms ( to: . movie) , let assetIdentifier = result. assetIdentifier {
352+ let fetchResult = PHAsset . fetchAssets ( withLocalIdentifiers: [ assetIdentifier] , options: nil )
353+ handleVideo ( selectedAsset: fetchResult. firstObject, utType: utType)
354+ picker. dismiss ( animated: true , completion: nil )
355+ return
356+ }
339357 }
358+ MDCSnackbarManager . default. show ( MDCSnackbarMessage ( text: " Could not handle asset types: \( itemProvider. registeredTypeIdentifiers) " ) )
340359 }
341- picker. dismiss ( animated: true , completion: nil )
342360 }
343361}
344362
@@ -377,18 +395,16 @@ extension AttachmentCreationCoordinator: UIImagePickerControllerDelegate {
377395 func handleCameraImage( picker: UIImagePickerController , info: [ UIImagePickerController . InfoKey : Any ] ) {
378396 locationManager? . stopUpdatingHeading ( ) ;
379397 locationManager? . stopUpdatingLocation ( ) ;
380- let uniqueId = UUID ( ) . uuidString
381- let date = Date ( )
382398 let dateFormatter = DateFormatter ( ) ;
383399 dateFormatter. dateFormat = " yyyyMMdd_HHmmss " ;
384400
385401 if let chosenImage = info [ . originalImage] as? UIImage ,
386402 let documentsDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first {
387403 DispatchQueue . global ( qos: . userInitiated) . async { [ self ] in
388404 let attachmentsDirectory = documentsDirectory. appendingPathComponent ( " attachments " ) ;
389- let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( uniqueId ) .jpeg " ) ;
390- let originalFileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( uniqueId ) \( dateFormatter. string ( from: date ) ) _original.jpeg " ) ;
391-
405+ let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( dateFormatter . string ( from : Date ( ) ) ) .jpeg " ) ;
406+ let originalFileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( dateFormatter. string ( from: Date ( ) ) ) _original.jpeg " ) ;
407+
392408 do {
393409 try FileManager . default. createDirectory ( at: fileToWriteTo. deletingLastPathComponent ( ) , withIntermediateDirectories: true , attributes: [ . protectionKey : FileProtectionType . complete] ) ;
394410 guard let imageData = chosenImage. qualityScaled ( ) else { return } ;
@@ -412,13 +428,13 @@ extension AttachmentCreationCoordinator: UIImagePickerControllerDelegate {
412428 guard let originalWithGPS = writeMetadataIntoImageData ( imagedata: originalImageData, metadata: NSDictionary ( dictionary: metadata) ) else { return } ;
413429 do {
414430 try originalWithGPS. write ( to: originalFileToWriteTo, options: . completeFileProtection)
415-
431+
416432 try ? PHPhotoLibrary . shared ( ) . performChangesAndWait {
417433 PHAssetChangeRequest . creationRequestForAssetFromImage ( atFileURL: originalFileToWriteTo)
418434 }
419435
420436 try FileManager . default. removeItem ( at: originalFileToWriteTo) ;
421-
437+
422438 } catch {
423439 MageLogger . misc. error ( " Unable to write image to file \( originalFileToWriteTo) : \( error) " )
424440 }
@@ -516,18 +532,19 @@ extension AttachmentCreationCoordinator: UIImagePickerControllerDelegate {
516532 }
517533 return nil ;
518534 }
519-
535+
520536 func handleMovie( picker: UIImagePickerController , info: [ UIImagePickerController . InfoKey : Any ] ) {
521537 MageLogger . misc. debug ( " handling movie \( info) " )
522- let uniqueId = UUID ( ) . uuidString
538+ let dateFormatter = DateFormatter ( ) ;
539+ dateFormatter. dateFormat = " yyyyMMdd_HHmmss " ;
523540 guard let videoUrl = info [ . mediaURL] as? URL else { return }
524541 guard let documentsDirectory = FileManager . default. urls ( for: . documentDirectory, in: . userDomainMask) . first else { return }
525542
526543 if ( picker. sourceType == . camera && UIVideoAtPathIsCompatibleWithSavedPhotosAlbum ( videoUrl. path) ) {
527544 UISaveVideoAtPathToSavedPhotosAlbum ( videoUrl. path, nil , nil , nil ) ;
528545 }
529546 let attachmentsDirectory = documentsDirectory. appendingPathComponent ( " attachments " ) ;
530- let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( uniqueId ) .mp4 " ) ;
547+ let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( dateFormatter . string ( from : Date ( ) ) ) .mp4 " ) ;
531548
532549 let videoQuality : String = videoUploadQuality ( ) ;
533550 MageLogger . misc. debug ( " video quality \( videoQuality) " )
@@ -546,22 +563,22 @@ extension AttachmentCreationCoordinator: UIImagePickerControllerDelegate {
546563 exportSession. exportAsynchronously {
547564 let foo = exportSession. status
548565 switch ( exportSession. status) {
549- case . completed:
550- print ( " Export complete " )
551- self . addAttachmentForSaving ( location: fileToWriteTo, contentType: " video/mp4 " )
552- case . failed:
553- print ( " Export Failed: \( String ( describing: exportSession. error? . localizedDescription) ) " )
554- case . cancelled:
555- print ( " Export cancelled " ) ;
556- case . unknown:
557- print ( " Unknown " )
558- case . waiting:
559- print ( " Waiting " )
560- case . exporting:
561- print ( " Exporting " )
562- @unknown default :
563- print ( " Unknown " )
564- }
566+ case . completed:
567+ print ( " Export complete " )
568+ self . addAttachmentForSaving ( location: fileToWriteTo, contentType: " video/mp4 " )
569+ case . failed:
570+ print ( " Export Failed: \( String ( describing: exportSession. error? . localizedDescription) ) " )
571+ case . cancelled:
572+ print ( " Export cancelled " ) ;
573+ case . unknown:
574+ print ( " Unknown " )
575+ case . waiting:
576+ print ( " Waiting " )
577+ case . exporting:
578+ print ( " Exporting " )
579+ @unknown default :
580+ print ( " Unknown " )
581+ }
565582 }
566583 } catch {
567584 MageLogger . misc. error ( " Error creating directory path \( fileToWriteTo. deletingLastPathComponent ( ) ) : \( error) " )
@@ -586,8 +603,12 @@ extension AttachmentCreationCoordinator: UIDocumentPickerDelegate {
586603 func documentPicker( _ controller: UIDocumentPickerViewController , didPickDocumentsAt urls: [ URL ] ) {
587604 for url in urls {
588605 let securityScoped = url. startAccessingSecurityScopedResource ( )
589- let uniqueId = UUID ( ) . uuidString
606+
607+ let dateFormatter = DateFormatter ( ) ;
608+ dateFormatter. dateFormat = " yyyyMMdd_HHmmss " ;
609+
590610 let uttype = try ? url. resourceValues ( forKeys: [ . contentTypeKey] ) . contentType
611+
591612 let fileType = uttype? . preferredFilenameExtension ?? url. pathExtension
592613 let mimeType = uttype? . preferredMIMEType ?? UTType . data. identifier
593614
@@ -598,11 +619,11 @@ extension AttachmentCreationCoordinator: UIDocumentPickerDelegate {
598619 let attachmentsDirectory = documentsDirectory. appendingPathComponent ( " attachments " )
599620 let urlWithoutExtension = url. deletingPathExtension ( )
600621 let filename = urlWithoutExtension. lastPathComponent
601- let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( filename) _ \( uniqueId ) . \( fileType) " ) ;
622+ let fileToWriteTo = attachmentsDirectory. appendingPathComponent ( " MAGE_ \( filename) _ \( dateFormatter . string ( from : Date ( ) ) ) . \( fileType) " ) ;
602623
603624 do {
604625 try FileManager . default. createDirectory ( at: fileToWriteTo. deletingLastPathComponent ( ) , withIntermediateDirectories: true , attributes: [ . protectionKey : FileProtectionType . complete] ) ;
605-
626+
606627
607628 do {
608629 let attachmentData = try Data ( contentsOf: url)
@@ -632,7 +653,7 @@ extension AttachmentCreationCoordinator: AudioRecordingDelegate {
632653 func recordingAvailable( recording: Recording ) {
633654 MageLogger . misc. debug ( " Recording available " )
634655 addAttachmentForSaving ( location: URL ( fileURLWithPath: recording. filePath!) , contentType: recording. mediaType!)
635-
656+
636657 self . audioRecorderViewController? . dismiss ( animated: true , completion: nil ) ;
637658 }
638659}
0 commit comments