소스 검색

File extension guessing

onevcat 1 년 전
부모
커밋
51a51e39ff

+ 46 - 3
Sources/General/ImageSource/LivePhotoSource.swift

@@ -26,15 +26,58 @@
 
 import Foundation
 
+public struct LivePhotoResource: Sendable {
+    
+    public enum FileType: Sendable {
+        case heic
+        case mov
+    }
+    
+    public let resource: any Resource
+    public let referenceFileType: FileType
+    
+    var cacheKey: String { resource.cacheKey }
+    var downloadURL: URL { resource.downloadURL }
+    
+    public init(downloadURL: URL, cacheKey: String? = nil, fileType: FileType? = nil) {
+        resource = KF.ImageResource(downloadURL: downloadURL, cacheKey: cacheKey)
+        referenceFileType = fileType ?? resource.guessedFileType
+    }
+    
+    public init(resource: any Resource, fileType: FileType? = nil) {
+        self.resource = resource
+        referenceFileType = fileType ?? resource.guessedFileType
+    }
+}
+
+extension Resource {
+    var guessedFileType: LivePhotoResource.FileType {
+        let pathExtension = downloadURL.pathExtension.lowercased()
+        switch pathExtension {
+        case "mov": return .mov
+        case "heic": return .heic
+        default:
+            assertionFailure("Explicit file type is necessary in the download URL as its extension. Otherwise, set the file type of the LivePhoto resource manually with `LivePhotoSource.init(resources:)`.")
+            return .heic
+        }
+    }
+}
+
 public struct LivePhotoSource: Sendable {
     
-    public let resources: [any Resource]
+    public let resources: [LivePhotoResource]
     
     public init(resources: [any Resource]) {
-        self.resources = resources
+        let livePhotoResources = resources.map { LivePhotoResource(resource: $0) }
+        self.init(livePhotoResources)
     }
     
     public init(urls: [URL]) {
-        self.resources = urls.map { KF.ImageResource(downloadURL: $0) }
+        let resources = urls.map { KF.ImageResource(downloadURL: $0) }
+        self.init(resources: resources)
+    }
+    
+    public init(_ resources: [LivePhotoResource]) {
+        self.resources = resources
     }
 }

+ 6 - 0
Sources/General/Kingfisher.swift

@@ -114,6 +114,12 @@ extension NSTextAttachment          : KingfisherCompatible { }
 extension WKInterfaceImage          : KingfisherCompatible { }
 #endif
 
+#if canImport(PhotosUI)
+import PhotosUI
+extension PHLivePhotoView           : KingfisherCompatible { }
+#endif
+
+
 #if os(tvOS) && canImport(TVUIKit)
 @available(tvOS 12.0, *)
 extension TVMonogramView            : KingfisherCompatible { }

+ 3 - 3
Sources/General/KingfisherManager+LivePhoto.swift

@@ -107,8 +107,8 @@ extension KingfisherManager {
             })
     }
     
-    func missingResources(_ source: LivePhotoSource, options: KingfisherParsedOptionsInfo) -> [any Resource] {
-        let missingResources: [any Resource]
+    func missingResources(_ source: LivePhotoSource, options: KingfisherParsedOptionsInfo) -> [LivePhotoResource] {
+        let missingResources: [LivePhotoResource]
         if options.forceRefresh {
             missingResources = source.resources
         } else {
@@ -130,7 +130,7 @@ extension KingfisherManager {
     }
     
     func downloadAndCache(
-        resources: [any Resource],
+        resources: [LivePhotoResource],
         options: KingfisherParsedOptionsInfo
     ) async throws -> [LivePhotoResourceDownloadingResult] {
         if resources.isEmpty {

+ 28 - 25
Tests/KingfisherTests/KingfisherManagerTests.swift

@@ -1353,7 +1353,7 @@ class KingfisherManagerTests: XCTestCase {
     }
     
     func testMissingResourceOfLivePhotoFound() {
-        let resource = KF.ImageResource(downloadURL: testURLs[0])
+        let resource = KF.ImageResource(downloadURL: LivePhotoURL.mov)
         let source = LivePhotoSource(resources: [resource])
         
         let missing = manager.missingResources(source, options: .init(.empty))
@@ -1361,7 +1361,7 @@ class KingfisherManagerTests: XCTestCase {
     }
     
     func testMissingResourceOfLivePhotoNotFound() async throws {
-        let resource = KF.ImageResource(downloadURL: testURLs[0])
+        let resource = KF.ImageResource(downloadURL: LivePhotoURL.mov)
         
         try await manager.cache.storeToDisk(testImageData, forKey: resource.cacheKey)
         
@@ -1371,8 +1371,8 @@ class KingfisherManagerTests: XCTestCase {
     }
     
     func testMissingResourceOfLivePhotoFoundOne() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
         
         try await manager.cache.storeToDisk(testImageData, forKey: resource1.cacheKey)
         
@@ -1383,18 +1383,21 @@ class KingfisherManagerTests: XCTestCase {
     }
     
     func testDownloadAndCacheLivePhotoResourcesAll() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
         
         stub(resource1.downloadURL, data: testImageData)
         stub(resource2.downloadURL, data: testImageData)
         
-        let result = try await manager.downloadAndCache(resources: [resource1, resource2], options: .init(.empty))
+        let result = try await manager.downloadAndCache(
+            resources: [resource1, resource2].map { LivePhotoResource.init(resource: $0)
+            },
+            options: .init(.empty))
         XCTAssertEqual(result.count, 2)
         
         let urls = result.compactMap(\.url)
-        XCTAssertTrue(urls.contains(testURLs[0]))
-        XCTAssertTrue(urls.contains(testURLs[1]))
+        XCTAssertTrue(urls.contains(LivePhotoURL.mov))
+        XCTAssertTrue(urls.contains(LivePhotoURL.heic))
         
         let resourceCached1 = manager.cache.imageCachedType(forKey: resource1.cacheKey)
         let resourceCached2 = manager.cache.imageCachedType(forKey: resource1.cacheKey)
@@ -1403,8 +1406,8 @@ class KingfisherManagerTests: XCTestCase {
     }
     
     func testRetrieveLivePhotoFromNetwork() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
         
         stub(resource1.downloadURL, data: testImageData)
         stub(resource2.downloadURL, data: testImageData)
@@ -1429,13 +1432,13 @@ class KingfisherManagerTests: XCTestCase {
         XCTAssertEqual(result.cacheType, .none)
         XCTAssertEqual(result.data(), [testImageData, testImageData])
         let urlsInSource = result.source.resources.map(\.downloadURL)
-        XCTAssertTrue(urlsInSource.contains(testURLs[0]))
-        XCTAssertTrue(urlsInSource.contains(testURLs[1]))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.mov))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.heic))
     }
     
     func testRetrieveLivePhotoFromLocal() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
         
         try await manager.cache.storeToDisk(
             testImageData,
@@ -1468,13 +1471,13 @@ class KingfisherManagerTests: XCTestCase {
         XCTAssertEqual(result.cacheType, .disk)
         XCTAssertEqual(result.data(), [])
         let urlsInSource = result.source.resources.map(\.downloadURL)
-        XCTAssertTrue(urlsInSource.contains(testURLs[0]))
-        XCTAssertTrue(urlsInSource.contains(testURLs[1]))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.mov))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.heic))
     }
     
     func testRetrieveLivePhotoMixed() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
         
         try await manager.cache.storeToDisk(
             testImageData,
@@ -1503,13 +1506,13 @@ class KingfisherManagerTests: XCTestCase {
         XCTAssertEqual(result.cacheType, .none)
         XCTAssertEqual(result.data(), [testImageData])
         let urlsInSource = result.source.resources.map(\.downloadURL)
-        XCTAssertTrue(urlsInSource.contains(testURLs[0]))
-        XCTAssertTrue(urlsInSource.contains(testURLs[1]))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.mov))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.heic))
     }
     
     func testRetrieveLivePhotoNetworkThenCache() async throws {
-        let resource1 = KF.ImageResource(downloadURL: testURLs[0])
-        let resource2 = KF.ImageResource(downloadURL: testURLs[1])
+        let resource1 = KF.ImageResource(downloadURL: LivePhotoURL.mov)
+        let resource2 = KF.ImageResource(downloadURL: LivePhotoURL.heic)
         
         stub(resource1.downloadURL, data: testImageData)
         stub(resource2.downloadURL, data: testImageData)
@@ -1534,8 +1537,8 @@ class KingfisherManagerTests: XCTestCase {
         XCTAssertEqual(result.cacheType, .none)
         XCTAssertEqual(result.data(), [testImageData, testImageData])
         let urlsInSource = result.source.resources.map(\.downloadURL)
-        XCTAssertTrue(urlsInSource.contains(testURLs[0]))
-        XCTAssertTrue(urlsInSource.contains(testURLs[1]))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.mov))
+        XCTAssertTrue(urlsInSource.contains(LivePhotoURL.heic))
         
         let localResult = try await manager.retrieveLivePhoto(with: source)
         XCTAssertEqual(localResult.fileURLs.count, 2)

+ 5 - 0
Tests/KingfisherTests/KingfisherTestHelper.swift

@@ -84,6 +84,11 @@ let testKeys = [
     "http://onevcat.com/content/images/2014/May/200.jpg?fads#kj1asf"
 ]
 
+enum LivePhotoURL {
+    static let mov = URL(string: "https://example.com/sample.mov")!
+    static let heic = URL(string: "https://example.com/sample.heic")!
+}
+
 let testURLs = testKeys.map { URL(string: $0)! }
 
 func cleanDefaultCache() {