From 7403e70893e9e5e1280efdcba9e9dedcb2c6903c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20B=C3=B6ttger?= Date: Sun, 29 Jun 2025 18:58:58 +0200 Subject: [PATCH] Set creation date for images using EXIF --- NearbyShare/InboundNearbyConnection.swift | 52 ++++++++++++++++++++++- QuickDrop.xcodeproj/project.pbxproj | 8 ++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/NearbyShare/InboundNearbyConnection.swift b/NearbyShare/InboundNearbyConnection.swift index 06a6742..770d5c8 100644 --- a/NearbyShare/InboundNearbyConnection.swift +++ b/NearbyShare/InboundNearbyConnection.swift @@ -11,7 +11,7 @@ import CryptoKit import Foundation import Network import System - +import ImageIO import BigInt import SwiftECC @@ -518,6 +518,9 @@ public class SaveFilesManager { public func registerFileFinishedDownloading(_ fileURL: URL) { filesFinishedDownloading.append(fileURL) filesFinishedDownloadingSinceLastRun.append(fileURL) + + // read out EXIF timestamp and manually set file creation date to it + applyEXIFTimestamps(at: fileURL) } public func movePendingFilesToTarget() { @@ -633,6 +636,53 @@ public class SaveFilesManager { fatalError("Failed to get downloads directory: \(error)") } } + + /// Returns the EXIF original date of an image file, adjusted to the current time zone. + private func exifOriginalDate(from url: URL) -> Date? { + guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil), + let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [CFString: Any], + let exif = properties[kCGImagePropertyExifDictionary] as? [CFString: Any], + let dateString = exif[kCGImagePropertyExifDateTimeOriginal] as? String + else { + return nil + } + + let formatter = DateFormatter() + formatter.dateFormat = "yyyy:MM:dd HH:mm:ss" + formatter.timeZone = TimeZone.current // Use the current device time zone + return formatter.date(from: dateString) + } + + + /// Applies EXIF timestamps to a given file or directory. + private func applyEXIFTimestamps(at url: URL) { + let fm = FileManager.default + var isDir: ObjCBool = false + guard fm.fileExists(atPath: url.path, isDirectory: &isDir) else { return } + + if isDir.boolValue { + // It's a directory — enumerate files + if let enumerator = fm.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey]) { + for case let fileURL as URL in enumerator { + guard (try? fileURL.resourceValues(forKeys: [.isRegularFileKey]))?.isRegularFile == true else { continue } + applyEXIFTimestamp(to: fileURL) + } + } + } else { + // It's a single file + applyEXIFTimestamp(to: url) + } + } + + /// Applies EXIF timestamp (if available) to a single file + private func applyEXIFTimestamp(to fileURL: URL) { + guard let exifDate = exifOriginalDate(from: fileURL) else { + return + } + + // Set the "creation date" + try? FileManager.default.setAttributes([.creationDate: exifDate], ofItemAtPath: fileURL.path) + } } extension Data { diff --git a/QuickDrop.xcodeproj/project.pbxproj b/QuickDrop.xcodeproj/project.pbxproj index 3d164a3..6a20336 100644 --- a/QuickDrop.xcodeproj/project.pbxproj +++ b/QuickDrop.xcodeproj/project.pbxproj @@ -825,8 +825,8 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; - SHARED_BUILD = 46; - SHARED_VERSION = 1.9.4; + SHARED_BUILD = 47; + SHARED_VERSION = 1.10; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; @@ -884,8 +884,8 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; - SHARED_BUILD = 46; - SHARED_VERSION = 1.9.4; + SHARED_BUILD = 47; + SHARED_VERSION = 1.10; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; };